Merge "liblog: gtest open("/dev/pmsg0", "a")"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 7f9536e..b3661e4 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -55,3 +55,4 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/lmkd_intermediates/import_includes)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libsysutils_intermediates/import_includes)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/grep $(PRODUCT_OUT)/system/bin/toolbox)
diff --git a/adb/.clang-format b/adb/.clang-format
index 2b83a1f..0395c8e 100644
--- a/adb/.clang-format
+++ b/adb/.clang-format
@@ -4,8 +4,8 @@
CommentPragmas: NOLINT:.*
DerivePointerAlignment: false
-IndentWidth: 2
+IndentWidth: 4
PointerAlignment: Left
-TabWidth: 2
+TabWidth: 4
UseTab: Never
PenaltyExcessCharacter: 32
diff --git a/adb/Android.mk b/adb/Android.mk
index 8d38077..4ffb589 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -5,7 +5,18 @@
LOCAL_PATH:= $(call my-dir)
-ADB_CLANG :=
+ifeq ($(HOST_OS),windows)
+ adb_host_clang := false # libc++ for mingw not ready yet.
+else
+ adb_host_clang := true
+endif
+
+adb_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android
+
+ADB_COMMON_CFLAGS := \
+ -Wall -Werror \
+ -Wno-unused-parameter \
+ -DADB_REVISION='"$(adb_version)"' \
# libadb
# =========================================================
@@ -21,21 +32,26 @@
adb_auth.cpp \
adb_io.cpp \
adb_listeners.cpp \
+ adb_utils.cpp \
sockets.cpp \
transport.cpp \
transport_local.cpp \
transport_usb.cpp \
+LIBADB_TEST_SRCS := \
+ adb_io_test.cpp \
+ adb_utils_test.cpp \
+ transport_test.cpp \
+
LIBADB_CFLAGS := \
- -Wall -Werror \
- -Wno-unused-parameter \
+ $(ADB_COMMON_CFLAGS) \
-Wno-missing-field-initializers \
-fvisibility=hidden \
LIBADB_darwin_SRC_FILES := \
fdevent.cpp \
- get_my_path_darwin.c \
- usb_osx.c \
+ get_my_path_darwin.cpp \
+ usb_osx.cpp \
LIBADB_linux_SRC_FILES := \
fdevent.cpp \
@@ -44,11 +60,11 @@
LIBADB_windows_SRC_FILES := \
get_my_path_windows.cpp \
- sysdeps_win32.c \
+ sysdeps_win32.cpp \
usb_windows.cpp \
include $(CLEAR_VARS)
-LOCAL_CLANG := $(ADB_CLANG)
+LOCAL_CLANG := true
LOCAL_MODULE := libadbd
LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=0
LOCAL_SRC_FILES := \
@@ -57,12 +73,14 @@
fdevent.cpp \
jdwp_service.cpp \
qemu_tracing.cpp \
- usb_linux_client.c \
+ usb_linux_client.cpp \
+
+LOCAL_SHARED_LIBRARIES := libbase
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
-LOCAL_CLANG := $(ADB_CLANG)
+LOCAL_CLANG := $(adb_host_clang)
LOCAL_MODULE := libadb
LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=1
LOCAL_SRC_FILES := \
@@ -70,22 +88,23 @@
$(LIBADB_$(HOST_OS)_SRC_FILES) \
adb_auth_host.cpp \
+LOCAL_SHARED_LIBRARIES := libbase
+
# Even though we're building a static library (and thus there's no link step for
# this to take effect), this adds the SSL includes to our path.
LOCAL_STATIC_LIBRARIES := libcrypto_static
ifeq ($(HOST_OS),windows)
LOCAL_C_INCLUDES += development/host/windows/usb/api/
+ # Windows.h defines an awful ERROR macro that collides with base/logging.h.
+ # Suppress it with NOGDI.
+ LOCAL_CFLAGS += -DNOGDI
endif
include $(BUILD_HOST_STATIC_LIBRARY)
-LIBADB_TEST_SRCS := \
- adb_io_test.cpp \
- transport_test.cpp \
-
include $(CLEAR_VARS)
-LOCAL_CLANG := $(ADB_CLANG)
+LOCAL_CLANG := true
LOCAL_MODULE := adbd_test
LOCAL_CFLAGS := -DADB_HOST=0 $(LIBADB_CFLAGS)
LOCAL_SRC_FILES := $(LIBADB_TEST_SRCS)
@@ -93,8 +112,9 @@
LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
include $(BUILD_NATIVE_TEST)
+ifneq ($(HOST_OS),windows)
include $(CLEAR_VARS)
-LOCAL_CLANG := $(ADB_CLANG)
+LOCAL_CLANG := $(adb_host_clang)
LOCAL_MODULE := adb_test
LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
LOCAL_SRC_FILES := $(LIBADB_TEST_SRCS) services.cpp
@@ -105,38 +125,57 @@
libcutils \
ifeq ($(HOST_OS),linux)
- LOCAL_LDLIBS += -lrt -ldl -lpthread
+ LOCAL_LDLIBS += -lrt -ldl -lpthread
+endif
+
+ifeq ($(HOST_OS),darwin)
+ LOCAL_LDLIBS += -framework CoreFoundation -framework IOKit
endif
include $(BUILD_HOST_NATIVE_TEST)
+endif
+
+# adb device tracker (used by ddms) test tool
+# =========================================================
+
+ifeq ($(HOST_OS),linux)
+include $(CLEAR_VARS)
+LOCAL_CLANG := $(adb_host_clang)
+LOCAL_MODULE := adb_device_tracker_test
+LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
+LOCAL_SRC_FILES := test_track_devices.cpp
+LOCAL_SHARED_LIBRARIES := liblog libbase
+LOCAL_STATIC_LIBRARIES := libadb libcrypto_static libcutils
+LOCAL_LDLIBS += -lrt -ldl -lpthread
+include $(BUILD_HOST_EXECUTABLE)
+endif
# adb host tool
# =========================================================
include $(CLEAR_VARS)
ifeq ($(HOST_OS),linux)
- LOCAL_LDLIBS += -lrt -ldl -lpthread
- LOCAL_CFLAGS += -DWORKAROUND_BUG6558362
+ LOCAL_LDLIBS += -lrt -ldl -lpthread
+ LOCAL_CFLAGS += -DWORKAROUND_BUG6558362
endif
ifeq ($(HOST_OS),darwin)
- LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
- LOCAL_CFLAGS += -Wno-sizeof-pointer-memaccess -Wno-unused-parameter
+ LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
+ LOCAL_CFLAGS += -Wno-sizeof-pointer-memaccess -Wno-unused-parameter
endif
ifeq ($(HOST_OS),windows)
- EXTRA_STATIC_LIBS := AdbWinApi
- ifneq ($(strip $(USE_MINGW)),)
- # MinGW under Linux case
+ # Windows.h defines an awful ERROR macro that collides with base/logging.h.
+ # Suppress it with NOGDI.
+ LOCAL_CFLAGS += -DNOGDI
LOCAL_LDLIBS += -lws2_32 -lgdi32
- USE_SYSDEPS_WIN32 := 1
- endif
+ EXTRA_STATIC_LIBS := AdbWinApi
endif
-LOCAL_CLANG := $(ADB_CLANG)
+LOCAL_CLANG := $(adb_host_clang)
LOCAL_SRC_FILES := \
- adb_main.cpp \
+ client/main.cpp \
console.cpp \
commandline.cpp \
adb_client.cpp \
@@ -144,8 +183,7 @@
file_sync_client.cpp \
LOCAL_CFLAGS += \
- -Wall -Werror \
- -Wno-unused-parameter \
+ $(ADB_COMMON_CFLAGS) \
-D_GNU_SOURCE \
-DADB_HOST=1 \
@@ -154,14 +192,22 @@
LOCAL_STATIC_LIBRARIES := \
libadb \
+ libbase \
libcrypto_static \
+ libcutils \
+ liblog \
$(EXTRA_STATIC_LIBS) \
-ifeq ($(USE_SYSDEPS_WIN32),)
- LOCAL_STATIC_LIBRARIES += libcutils
+# libc++ not available on windows yet
+ifneq ($(HOST_OS),windows)
+ LOCAL_CXX_STL := libc++_static
endif
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+# Don't add anything here, we don't want additional shared dependencies
+# on the host adb tool, and shared libraries that link against libc++
+# will violate ODR
+LOCAL_SHARED_LIBRARIES :=
+
include $(BUILD_HOST_EXECUTABLE)
$(call dist-for-goals,dist_files sdk,$(LOCAL_BUILT_MODULE))
@@ -178,10 +224,10 @@
include $(CLEAR_VARS)
-LOCAL_CLANG := $(ADB_CLANG)
+LOCAL_CLANG := true
LOCAL_SRC_FILES := \
- adb_main.cpp \
+ daemon/main.cpp \
services.cpp \
file_sync_service.cpp \
framebuffer_service.cpp \
@@ -189,10 +235,9 @@
set_verity_enable_state_service.cpp \
LOCAL_CFLAGS := \
+ $(ADB_COMMON_CFLAGS) \
-DADB_HOST=0 \
-D_GNU_SOURCE \
- -Wall -Werror \
- -Wno-unused-parameter \
-Wno-deprecated-declarations \
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
@@ -208,10 +253,11 @@
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
-LOCAL_C_INCLUDES += system/extras/ext4_utils system/core/fs_mgr/include
+LOCAL_C_INCLUDES += system/extras/ext4_utils
LOCAL_STATIC_LIBRARIES := \
libadbd \
+ libbase \
libfs_mgr \
liblog \
libcutils \
@@ -220,6 +266,4 @@
libselinux \
libext4_utils_static \
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
include $(BUILD_EXECUTABLE)
diff --git a/adb/adb.cpp b/adb/adb.cpp
index ffa93f4..c4e3434 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -14,21 +14,27 @@
* limitations under the License.
*/
-#define TRACE_TAG TRACE_ADB
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <stddef.h>
-#include <string.h>
-#include <time.h>
-#include <sys/time.h>
-#include <stdint.h>
+#define TRACE_TAG TRACE_ADB
#include "sysdeps.h"
#include "adb.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <string>
+
+#include <base/stringprintf.h>
+#include <base/strings.h>
+
#include "adb_auth.h"
#include "adb_io.h"
#include "adb_listeners.h"
@@ -42,9 +48,7 @@
#include <sys/mount.h>
#endif
-#if ADB_TRACE
ADB_MUTEX_DEFINE( D_lock );
-#endif
int HOST = 0;
@@ -74,17 +78,66 @@
exit(-1);
}
-int adb_trace_mask;
+#if !ADB_HOST
+void start_device_log(void) {
+ struct tm now;
+ time_t t;
+ tzset();
+ time(&t);
+ localtime_r(&t, &now);
-/* read a comma/space/colum/semi-column separated list of tags
- * from the ADB_TRACE environment variable and build the trace
- * mask from it. note that '1' and 'all' are special cases to
- * enable all tracing
- */
-void adb_trace_init(void)
-{
- const char* p = getenv("ADB_TRACE");
- const char* q;
+ char timestamp[PATH_MAX];
+ strftime(timestamp, sizeof(timestamp), "%Y-%m-%d-%H-%M-%S", &now);
+
+ std::string path = android::base::StringPrintf("/data/adb/adb-%s-%d", timestamp, getpid());
+ int fd = unix_open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
+ if (fd == -1) {
+ return;
+ }
+
+ // redirect stdout and stderr to the log file
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ fprintf(stderr, "--- adb starting (pid %d) ---\n", getpid());
+ adb_close(fd);
+}
+#endif
+
+int adb_trace_mask;
+
+std::string get_trace_setting_from_env() {
+ const char* setting = getenv("ADB_TRACE");
+ if (setting == nullptr) {
+ setting = "";
+ }
+
+ return std::string(setting);
+}
+
+#if !ADB_HOST
+std::string get_trace_setting_from_prop() {
+ char buf[PROPERTY_VALUE_MAX];
+ property_get("persist.adb.trace_mask", buf, "");
+ return std::string(buf);
+}
+#endif
+
+std::string get_trace_setting() {
+#if ADB_HOST
+ return get_trace_setting_from_env();
+#else
+ return get_trace_setting_from_prop();
+#endif
+}
+
+// Split the comma/space/colum/semi-column separated list of tags from the trace
+// setting and build the trace mask from it. note that '1' and 'all' are special
+// cases to enable all tracing.
+//
+// adb's trace setting comes from the ADB_TRACE environment variable, whereas
+// adbd's comes from the system property persist.adb.trace_mask.
+void adb_trace_init() {
+ const std::string trace_setting = get_trace_setting();
static const struct {
const char* tag;
@@ -106,25 +159,25 @@
{ NULL, 0 }
};
- if (p == NULL)
- return;
+ if (trace_setting.empty()) {
+ return;
+ }
- /* use a comma/column/semi-colum/space separated list */
+ // Use a comma/colon/semi-colon/space separated list
+ const char* p = trace_setting.c_str();
while (*p) {
int len, tagn;
- q = strpbrk(p, " ,:;");
+ const char* q = strpbrk(p, " ,:;");
if (q == NULL) {
q = p + strlen(p);
}
len = q - p;
- for (tagn = 0; tags[tagn].tag != NULL; tagn++)
- {
+ for (tagn = 0; tags[tagn].tag != NULL; tagn++) {
int taglen = strlen(tags[tagn].tag);
- if (len == taglen && !memcmp(tags[tagn].tag, p, len) )
- {
+ if (len == taglen && !memcmp(tags[tagn].tag, p, len)) {
int flag = tags[tagn].flag;
if (flag == 0) {
adb_trace_mask = ~0;
@@ -138,6 +191,10 @@
if (*p)
p++;
}
+
+#if !ADB_HOST
+ start_device_log();
+#endif
}
apacket* get_apacket(void)
@@ -261,28 +318,6 @@
#endif
}
-#if !ADB_HOST
-static void send_msg_with_header(int fd, const char* msg, size_t msglen) {
- char header[5];
- if (msglen > 0xffff)
- msglen = 0xffff;
- snprintf(header, sizeof(header), "%04x", (unsigned)msglen);
- WriteFdExactly(fd, header, 4);
- WriteFdExactly(fd, msg, msglen);
-}
-#endif
-
-#if ADB_HOST
-static void send_msg_with_okay(int fd, const char* msg, size_t msglen) {
- char header[9];
- if (msglen > 0xffff)
- msglen = 0xffff;
- snprintf(header, sizeof(header), "OKAY%04x", (unsigned)msglen);
- WriteFdExactly(fd, header, 8);
- WriteFdExactly(fd, msg, msglen);
-}
-#endif // ADB_HOST
-
void send_connect(atransport *t)
{
D("Calling send_connect \n");
@@ -295,113 +330,64 @@
send_packet(cp, t);
}
-#if ADB_HOST
-static const char* connection_state_name(atransport *t)
-{
- if (t == NULL) {
- return "unknown";
- }
-
- switch(t->connection_state) {
- case CS_BOOTLOADER:
- return "bootloader";
- case CS_DEVICE:
- return "device";
- case CS_RECOVERY:
- return "recovery";
- case CS_SIDELOAD:
- return "sideload";
- case CS_OFFLINE:
- return "offline";
- case CS_UNAUTHORIZED:
- return "unauthorized";
- default:
- return "unknown";
- }
-}
-#endif // ADB_HOST
-
-/* qual_overwrite is used to overwrite a qualifier string. dst is a
- * pointer to a char pointer. It is assumed that if *dst is non-NULL, it
- * was malloc'ed and needs to freed. *dst will be set to a dup of src.
- */
-static void qual_overwrite(char **dst, const char *src)
-{
- if (!dst)
- return;
-
+// qual_overwrite is used to overwrite a qualifier string. dst is a
+// pointer to a char pointer. It is assumed that if *dst is non-NULL, it
+// was malloc'ed and needs to freed. *dst will be set to a dup of src.
+// TODO: switch to std::string for these atransport fields instead.
+static void qual_overwrite(char** dst, const std::string& src) {
free(*dst);
- *dst = NULL;
-
- if (!src || !*src)
- return;
-
- *dst = strdup(src);
+ *dst = strdup(src.c_str());
}
-void parse_banner(char *banner, atransport *t)
-{
- static const char *prop_seps = ";";
- static const char key_val_sep = '=';
- char *cp;
- char *type;
-
+void parse_banner(const char* banner, atransport* t) {
D("parse_banner: %s\n", banner);
- type = banner;
- cp = strchr(type, ':');
- if (cp) {
- *cp++ = 0;
- /* Nothing is done with second field. */
- cp = strchr(cp, ':');
- if (cp) {
- char *save;
- char *key;
- key = adb_strtok_r(cp + 1, prop_seps, &save);
- while (key) {
- cp = strchr(key, key_val_sep);
- if (cp) {
- *cp++ = '\0';
- if (!strcmp(key, "ro.product.name"))
- qual_overwrite(&t->product, cp);
- else if (!strcmp(key, "ro.product.model"))
- qual_overwrite(&t->model, cp);
- else if (!strcmp(key, "ro.product.device"))
- qual_overwrite(&t->device, cp);
- }
- key = adb_strtok_r(NULL, prop_seps, &save);
+
+ // The format is something like:
+ // "device::ro.product.name=x;ro.product.model=y;ro.product.device=z;".
+ std::vector<std::string> pieces = android::base::Split(banner, ":");
+
+ if (pieces.size() > 2) {
+ const std::string& props = pieces[2];
+ for (auto& prop : android::base::Split(props, ";")) {
+ // The list of properties was traditionally ;-terminated rather than ;-separated.
+ if (prop.empty()) continue;
+
+ std::vector<std::string> key_value = android::base::Split(prop, "=");
+ if (key_value.size() != 2) continue;
+
+ const std::string& key = key_value[0];
+ const std::string& value = key_value[1];
+ if (key == "ro.product.name") {
+ qual_overwrite(&t->product, value);
+ } else if (key == "ro.product.model") {
+ qual_overwrite(&t->model, value);
+ } else if (key == "ro.product.device") {
+ qual_overwrite(&t->device, value);
}
}
}
- if(!strcmp(type, "bootloader")){
+ const std::string& type = pieces[0];
+ if (type == "bootloader") {
D("setting connection_state to CS_BOOTLOADER\n");
t->connection_state = CS_BOOTLOADER;
update_transports();
- return;
- }
-
- if(!strcmp(type, "device")) {
+ } else if (type == "device") {
D("setting connection_state to CS_DEVICE\n");
t->connection_state = CS_DEVICE;
update_transports();
- return;
- }
-
- if(!strcmp(type, "recovery")) {
+ } else if (type == "recovery") {
D("setting connection_state to CS_RECOVERY\n");
t->connection_state = CS_RECOVERY;
update_transports();
- return;
- }
-
- if(!strcmp(type, "sideload")) {
+ } else if (type == "sideload") {
D("setting connection_state to CS_SIDELOAD\n");
t->connection_state = CS_SIDELOAD;
update_transports();
- return;
+ } else {
+ D("setting connection_state to CS_HOST\n");
+ t->connection_state = CS_HOST;
}
-
- t->connection_state = CS_HOST;
}
void handle_packet(apacket *p, atransport *t)
@@ -433,7 +419,7 @@
handle_offline(t);
}
- parse_banner((char*) p->data, t);
+ parse_banner(reinterpret_cast<const char*>(p->data), t);
if (HOST || !auth_enabled) {
handle_online(t);
@@ -710,24 +696,15 @@
// Try to handle a network forwarding request.
// This returns 1 on success, 0 on failure, and -1 to indicate this is not
// a forwarding-related request.
-int handle_forward_request(const char* service, transport_type ttype, char* serial, int reply_fd)
+int handle_forward_request(const char* service, TransportType type, const char* serial, int reply_fd)
{
if (!strcmp(service, "list-forward")) {
// Create the list of forward redirections.
- int buffer_size = format_listeners(NULL, 0);
- // Add one byte for the trailing zero.
- char* buffer = reinterpret_cast<char*>(malloc(buffer_size + 1));
- if (buffer == nullptr) {
- sendfailmsg(reply_fd, "not enough memory");
- return 1;
- }
- (void) format_listeners(buffer, buffer_size + 1);
+ std::string listeners = format_listeners();
#if ADB_HOST
- send_msg_with_okay(reply_fd, buffer, buffer_size);
-#else
- send_msg_with_header(reply_fd, buffer, buffer_size);
+ SendOkay(reply_fd);
#endif
- free(buffer);
+ SendProtocolString(reply_fd, listeners);
return 1;
}
@@ -735,16 +712,15 @@
remove_all_listeners();
#if ADB_HOST
/* On the host: 1st OKAY is connect, 2nd OKAY is status */
- adb_write(reply_fd, "OKAY", 4);
+ SendOkay(reply_fd);
#endif
- adb_write(reply_fd, "OKAY", 4);
+ SendOkay(reply_fd);
return 1;
}
if (!strncmp(service, "forward:",8) ||
!strncmp(service, "killforward:",12)) {
char *local, *remote;
- int r;
atransport *transport;
int createForward = strncmp(service, "kill", 4);
@@ -763,72 +739,69 @@
if (createForward) {
// Check forward: parameter format: '<local>;<remote>'
if(remote == 0) {
- sendfailmsg(reply_fd, "malformed forward spec");
+ SendFail(reply_fd, "malformed forward spec");
return 1;
}
*remote++ = 0;
if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')) {
- sendfailmsg(reply_fd, "malformed forward spec");
+ SendFail(reply_fd, "malformed forward spec");
return 1;
}
} else {
// Check killforward: parameter format: '<local>'
if (local[0] == 0) {
- sendfailmsg(reply_fd, "malformed forward spec");
+ SendFail(reply_fd, "malformed forward spec");
return 1;
}
}
- const char* err;
- transport = acquire_one_transport(CS_ANY, ttype, serial, &err);
+ std::string error_msg;
+ transport = acquire_one_transport(CS_ANY, type, serial, &error_msg);
if (!transport) {
- sendfailmsg(reply_fd, err);
+ SendFail(reply_fd, error_msg);
return 1;
}
+ InstallStatus r;
if (createForward) {
r = install_listener(local, remote, transport, no_rebind);
} else {
r = remove_listener(local, transport);
}
- if(r == 0) {
+ if (r == INSTALL_STATUS_OK) {
#if ADB_HOST
/* On the host: 1st OKAY is connect, 2nd OKAY is status */
- WriteFdExactly(reply_fd, "OKAY", 4);
+ SendOkay(reply_fd);
#endif
- WriteFdExactly(reply_fd, "OKAY", 4);
+ SendOkay(reply_fd);
return 1;
}
- if (createForward) {
- const char* message;
- switch (r) {
- case INSTALL_STATUS_CANNOT_BIND:
- message = "cannot bind to socket";
- break;
- case INSTALL_STATUS_CANNOT_REBIND:
- message = "cannot rebind existing socket";
- break;
- default:
- message = "internal error";
- }
- sendfailmsg(reply_fd, message);
- } else {
- sendfailmsg(reply_fd, "cannot remove listener");
+ std::string message;
+ switch (r) {
+ case INSTALL_STATUS_OK: message = " "; break;
+ case INSTALL_STATUS_INTERNAL_ERROR: message = "internal error"; break;
+ case INSTALL_STATUS_CANNOT_BIND:
+ message = android::base::StringPrintf("cannot bind to socket: %s", strerror(errno));
+ break;
+ case INSTALL_STATUS_CANNOT_REBIND:
+ message = android::base::StringPrintf("cannot rebind existing socket: %s", strerror(errno));
+ break;
+ case INSTALL_STATUS_LISTENER_NOT_FOUND: message = "listener not found"; break;
}
+ SendFail(reply_fd, message);
return 1;
}
return 0;
}
-int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s)
-{
- if(!strcmp(service, "kill")) {
- fprintf(stderr,"adb server killed by remote request\n");
+int handle_host_request(const char* service, TransportType type,
+ const char* serial, int reply_fd, asocket* s) {
+ if (strcmp(service, "kill") == 0) {
+ fprintf(stderr, "adb server killed by remote request\n");
fflush(stdout);
- adb_write(reply_fd, "OKAY", 4);
- usb_cleanup();
+ SendOkay(reply_fd);
exit(0);
}
@@ -839,7 +812,7 @@
// "transport-local:" is used for switching transport to the only local transport
// "transport-any:" is used for switching transport to the only transport
if (!strncmp(service, "transport", strlen("transport"))) {
- transport_type type = kTransportAny;
+ TransportType type = kTransportAny;
if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
type = kTransportUsb;
@@ -852,37 +825,37 @@
serial = service;
}
- const char* error_string = "unknown failure";
- transport = acquire_one_transport(CS_ANY, type, serial, &error_string);
+ std::string error_msg = "unknown failure";
+ transport = acquire_one_transport(CS_ANY, type, serial, &error_msg);
if (transport) {
s->transport = transport;
- adb_write(reply_fd, "OKAY", 4);
+ SendOkay(reply_fd);
} else {
- sendfailmsg(reply_fd, error_string);
+ SendFail(reply_fd, error_msg);
}
return 1;
}
// return a list of all connected devices
if (!strncmp(service, "devices", 7)) {
- char buffer[4096];
- int use_long = !strcmp(service+7, "-l");
- if (use_long || service[7] == 0) {
- memset(buffer, 0, sizeof(buffer));
- D("Getting device list \n");
- list_transports(buffer, sizeof(buffer), use_long);
- D("Wrote device list \n");
- send_msg_with_okay(reply_fd, buffer, strlen(buffer));
+ bool long_listing = (strcmp(service+7, "-l") == 0);
+ if (long_listing || service[7] == 0) {
+ D("Getting device list...\n");
+ std::string device_list = list_transports(long_listing);
+ D("Sending device list...\n");
+ SendOkay(reply_fd);
+ SendProtocolString(reply_fd, device_list);
return 0;
}
+ return 1;
}
// remove TCP transport
if (!strncmp(service, "disconnect:", 11)) {
char buffer[4096];
memset(buffer, 0, sizeof(buffer));
- char* serial = service + 11;
+ const char* serial = service + 11;
if (serial[0] == 0) {
// disconnect from all TCP devices
unregister_all_tcp_transports();
@@ -902,34 +875,36 @@
}
}
- send_msg_with_okay(reply_fd, buffer, strlen(buffer));
+ SendOkay(reply_fd);
+ SendProtocolString(reply_fd, buffer);
return 0;
}
// returns our value for ADB_SERVER_VERSION
if (!strcmp(service, "version")) {
- char version[12];
- snprintf(version, sizeof version, "%04x", ADB_SERVER_VERSION);
- send_msg_with_okay(reply_fd, version, strlen(version));
+ SendOkay(reply_fd);
+ SendProtocolString(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
return 0;
}
if(!strncmp(service,"get-serialno",strlen("get-serialno"))) {
const char *out = "unknown";
- transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
- if (transport && transport->serial) {
+ transport = acquire_one_transport(CS_ANY, type, serial, NULL);
+ if (transport && transport->serial) {
out = transport->serial;
}
- send_msg_with_okay(reply_fd, out, strlen(out));
+ SendOkay(reply_fd);
+ SendProtocolString(reply_fd, out);
return 0;
}
if(!strncmp(service,"get-devpath",strlen("get-devpath"))) {
const char *out = "unknown";
- transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
- if (transport && transport->devpath) {
+ transport = acquire_one_transport(CS_ANY, type, serial, NULL);
+ if (transport && transport->devpath) {
out = transport->devpath;
}
- send_msg_with_okay(reply_fd, out, strlen(out));
+ SendOkay(reply_fd);
+ SendProtocolString(reply_fd, out);
return 0;
}
// indicates a new emulator instance has started
@@ -941,14 +916,14 @@
}
if(!strncmp(service,"get-state",strlen("get-state"))) {
- transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
- const char *state = connection_state_name(transport);
- send_msg_with_okay(reply_fd, state, strlen(state));
+ transport = acquire_one_transport(CS_ANY, type, serial, NULL);
+ SendOkay(reply_fd);
+ SendProtocolString(reply_fd, transport->connection_state_name());
return 0;
}
#endif // ADB_HOST
- int ret = handle_forward_request(service, ttype, serial, reply_fd);
+ int ret = handle_forward_request(service, type, serial, reply_fd);
if (ret >= 0)
return ret - 1;
return -1;
diff --git a/adb/adb.h b/adb/adb.h
index 749515c..7942a86 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -23,10 +23,6 @@
#include "adb_trace.h"
#include "fdevent.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
#define MAX_PAYLOAD 4096
#define A_SYNC 0x434e5953
@@ -47,14 +43,8 @@
// Increment this when we want to force users to start a new adb server.
#define ADB_SERVER_VERSION 32
-typedef struct amessage amessage;
-typedef struct apacket apacket;
-typedef struct asocket asocket;
-typedef struct alistener alistener;
-typedef struct aservice aservice;
-typedef struct atransport atransport;
-typedef struct adisconnect adisconnect;
-typedef struct usb_handle usb_handle;
+struct atransport;
+struct usb_handle;
struct amessage {
unsigned command; /* command identifier constant */
@@ -171,12 +161,12 @@
** object, it's a special value used to indicate that a client wants to
** connect to a service implemented within the ADB server itself.
*/
-typedef enum transport_type {
+enum TransportType {
kTransportUsb,
kTransportLocal,
kTransportAny,
kTransportHost,
-} transport_type;
+};
#define TOKEN_SIZE 20
@@ -197,7 +187,7 @@
unsigned sync_token;
int connection_state;
int online;
- transport_type type;
+ TransportType type;
/* usb handle or socket fd as needed */
usb_handle *usb;
@@ -219,6 +209,8 @@
unsigned char token[TOKEN_SIZE];
fdevent auth_fde;
unsigned failed_auth_attempts;
+
+ const char* connection_state_name() const;
};
@@ -253,8 +245,6 @@
void remove_socket(asocket *s);
void close_all_sockets(atransport *t);
-#define LOCAL_CLIENT_PREFIX "emulator-"
-
asocket *create_local_socket(int fd);
asocket *create_local_service_socket(const char *destination);
@@ -294,7 +284,7 @@
int create_jdwp_connection_fd(int jdwp_pid);
#endif
-int handle_forward_request(const char* service, transport_type ttype, char* serial, int reply_fd);
+int handle_forward_request(const char* service, TransportType type, const char* serial, int reply_fd);
#if !ADB_HOST
void framebuffer_service(int fd, void *cookie);
@@ -334,7 +324,6 @@
/* usb host/client interface */
void usb_init();
-void usb_cleanup();
int usb_write(usb_handle *h, const void *data, int len);
int usb_read(usb_handle *h, void *data, int len);
int usb_close(usb_handle *h);
@@ -363,11 +352,6 @@
extern int HOST;
extern int SHELL_EXIT_NOTIFY_FD;
-typedef enum {
- SUBPROC_PTY = 0,
- SUBPROC_RAW = 1,
-} subproc_mode;
-
#define CHUNK_SIZE (64*1024)
#if !ADB_HOST
@@ -381,16 +365,11 @@
#define USB_FFS_ADB_IN USB_FFS_ADB_EP(ep2)
#endif
-int sendfailmsg(int fd, const char *reason);
-int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s);
+int handle_host_request(const char* service, TransportType type, const char* serial, int reply_fd, asocket *s);
void handle_online(atransport *t);
void handle_offline(atransport *t);
void send_connect(atransport *t);
-#ifdef __cplusplus
-}
-#endif
-
#endif
diff --git a/adb/adb_auth.cpp b/adb/adb_auth.cpp
index c236b64..dc01825 100644
--- a/adb/adb_auth.cpp
+++ b/adb/adb_auth.cpp
@@ -14,7 +14,10 @@
* limitations under the License.
*/
-#define TRACE_TAG TRACE_ADB
+#define TRACE_TAG TRACE_ADB
+
+#include "sysdeps.h"
+#include "adb_auth.h"
#include <errno.h>
#include <stdio.h>
@@ -23,9 +26,7 @@
#include <unistd.h>
#include "adb.h"
-#include "adb_auth.h"
#include "transport.h"
-#include "sysdeps.h"
int auth_enabled = 0;
diff --git a/adb/adb_auth.h b/adb/adb_auth.h
index e0425ad..1e1978d 100644
--- a/adb/adb_auth.h
+++ b/adb/adb_auth.h
@@ -17,9 +17,7 @@
#ifndef __ADB_AUTH_H
#define __ADB_AUTH_H
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include "adb.h"
extern int auth_enabled;
@@ -66,8 +64,4 @@
#endif // ADB_HOST
-#ifdef __cplusplus
-}
-#endif
-
#endif // __ADB_AUTH_H
diff --git a/adb/adb_auth_client.cpp b/adb/adb_auth_client.cpp
index 5dadcd9..8e7d38b 100644
--- a/adb/adb_auth_client.cpp
+++ b/adb/adb_auth_client.cpp
@@ -14,24 +14,24 @@
* limitations under the License.
*/
+#define TRACE_TAG TRACE_AUTH
+
+#include "sysdeps.h"
+#include "adb_auth.h"
+
#include <resolv.h>
#include <stdio.h>
#include <string.h>
-#include "sysdeps.h"
-
-#include "adb.h"
-#include "adb_auth.h"
#include "cutils/list.h"
#include "cutils/sockets.h"
-#include "fdevent.h"
#include "mincrypt/rsa.h"
#include "mincrypt/sha.h"
+
+#include "adb.h"
+#include "fdevent.h"
#include "transport.h"
-#define TRACE_TAG TRACE_AUTH
-
-
struct adb_public_key {
struct listnode node;
RSAPublicKey key;
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp
index aba23d4..e4658f5 100644
--- a/adb/adb_auth_host.cpp
+++ b/adb/adb_auth_host.cpp
@@ -14,8 +14,19 @@
* limitations under the License.
*/
+#define TRACE_TAG TRACE_AUTH
+
+#ifdef _WIN32
+// This blocks some definitions we need on Windows.
+#undef NOGDI
+#endif
+
+#include "sysdeps.h"
+#include "adb_auth.h"
+
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#ifdef _WIN32
# ifndef WIN32_LEAN_AND_MEAN
@@ -28,11 +39,8 @@
# include <sys/stat.h>
# include <unistd.h>
#endif
-#include <string.h>
-#include "sysdeps.h"
#include "adb.h"
-#include "adb_auth.h"
/* HACK: we need the RSAPublicKey struct
* but RSA_verify conflits with openssl */
@@ -40,6 +48,7 @@
#include "mincrypt/rsa.h"
#undef RSA_verify
+#include <base/strings.h>
#include <cutils/list.h>
#include <openssl/evp.h>
@@ -52,12 +61,9 @@
#include <openssl/base64.h>
#endif
-#define TRACE_TAG TRACE_AUTH
-
#define ANDROID_PATH ".android"
#define ADB_KEY_FILE "adbkey"
-
struct adb_private_key {
struct listnode node;
RSA *rsa;
@@ -172,7 +178,7 @@
return 0;
}
- outfile = fopen(path, "w");
+ outfile = fopen(path, "we");
if (!outfile) {
D("Failed to open '%s'\n", path);
return 0;
@@ -191,7 +197,7 @@
encoded_length = 1 + ((sizeof(pkey) + 2) / 3 * 4);
#endif
- encoded = reinterpret_cast<uint8_t*>(malloc(encoded_length));
+ encoded = new uint8_t[encoded_length];
if (encoded == nullptr) {
D("Allocation failure");
goto out;
@@ -212,9 +218,7 @@
if (outfile != NULL) {
fclose(outfile);
}
- if (encoded != NULL) {
- free(encoded);
- }
+ delete[] encoded;
return ret;
}
@@ -240,7 +244,7 @@
old_mask = umask(077);
- f = fopen(file, "w");
+ f = fopen(file, "we");
if (!f) {
D("Failed to open '%s'\n", file);
umask(old_mask);
@@ -274,30 +278,24 @@
{
D("read_key '%s'\n", file);
- FILE* f = fopen(file, "r");
- if (!f) {
- D("Failed to open '%s'\n", file);
+ FILE* fp = fopen(file, "re");
+ if (!fp) {
+ D("Failed to open '%s': %s\n", file, strerror(errno));
return 0;
}
- adb_private_key* key = reinterpret_cast<adb_private_key*>(
- malloc(sizeof(adb_private_key)));
- if (!key) {
- D("Failed to alloc key\n");
- fclose(f);
- return 0;
- }
+ adb_private_key* key = new adb_private_key;
key->rsa = RSA_new();
- if (!PEM_read_RSAPrivateKey(f, &key->rsa, NULL, NULL)) {
+ if (!PEM_read_RSAPrivateKey(fp, &key->rsa, NULL, NULL)) {
D("Failed to read key\n");
- fclose(f);
+ fclose(fp);
RSA_free(key->rsa);
- free(key);
+ delete key;
return 0;
}
- fclose(f);
+ fclose(fp);
list_add_tail(list, &key->node);
return 1;
}
@@ -362,29 +360,16 @@
return read_key(path, list);
}
-static void get_vendor_keys(struct listnode *list)
-{
- const char *adb_keys_path;
- char keys_path[MAX_PAYLOAD];
- char *path;
- char *save;
- struct stat buf;
-
- adb_keys_path = getenv("ADB_VENDOR_KEYS");
- if (!adb_keys_path)
+static void get_vendor_keys(struct listnode* key_list) {
+ const char* adb_keys_path = getenv("ADB_VENDOR_KEYS");
+ if (adb_keys_path == nullptr) {
return;
- strncpy(keys_path, adb_keys_path, sizeof(keys_path));
+ }
- path = adb_strtok_r(keys_path, ENV_PATH_SEPARATOR_STR, &save);
- while (path) {
- D("Reading: '%s'\n", path);
-
- if (stat(path, &buf))
- D("Can't read '%s'\n", path);
- else if (!read_key(path, list))
- D("Failed to read '%s'\n", path);
-
- path = adb_strtok_r(NULL, ENV_PATH_SEPARATOR_STR, &save);
+ for (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'\n", path.c_str());
+ }
}
}
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index a485aa2..532af45 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -14,6 +14,11 @@
* limitations under the License.
*/
+#define TRACE_TAG TRACE_ADB
+
+#include "sysdeps.h"
+#include "adb_client.h"
+
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
@@ -23,19 +28,43 @@
#include <sys/stat.h>
#include <sys/types.h>
-#include "sysdeps.h"
+#include <string>
+#include <vector>
-#define TRACE_TAG TRACE_ADB
-#include "adb_client.h"
+#include <base/stringprintf.h>
+#include <base/strings.h>
+
#include "adb_io.h"
-static transport_type __adb_transport = kTransportAny;
+static TransportType __adb_transport = kTransportAny;
static const char* __adb_serial = NULL;
static int __adb_server_port = DEFAULT_ADB_PORT;
static const char* __adb_server_name = NULL;
-void adb_set_transport(transport_type type, const char* serial)
+static std::string perror_str(const char* msg) {
+ return android::base::StringPrintf("%s: %s", msg, strerror(errno));
+}
+
+static bool ReadProtocolString(int fd, std::string* s, std::string* error) {
+ char buf[5];
+ if (!ReadFdExactly(fd, buf, 4)) {
+ *error = perror_str("protocol fault (couldn't read status length)");
+ return false;
+ }
+ buf[4] = 0;
+
+ unsigned long len = strtoul(buf, 0, 16);
+ s->resize(len, '\0');
+ if (!ReadFdExactly(fd, &(*s)[0], len)) {
+ *error = perror_str("protocol fault (couldn't read status message)");
+ return false;
+ }
+
+ return true;
+}
+
+void adb_set_transport(TransportType type, const char* serial)
{
__adb_transport = type;
__adb_serial = serial;
@@ -51,179 +80,98 @@
__adb_server_name = hostname;
}
-int adb_get_emulator_console_port(void)
-{
- const char* serial = __adb_serial;
- int port;
-
- if (serial == NULL) {
- /* if no specific device was specified, we need to look at */
- /* the list of connected devices, and extract an emulator */
- /* name from it. two emulators is an error */
- char* tmp = adb_query("host:devices");
- char* p = tmp;
- if(!tmp) {
- printf("no emulator connected\n");
- return -1;
- }
- while (*p) {
- char* q = strchr(p, '\n');
- if (q != NULL)
- *q++ = 0;
- else
- q = p + strlen(p);
-
- if (!memcmp(p, LOCAL_CLIENT_PREFIX, sizeof(LOCAL_CLIENT_PREFIX)-1)) {
- if (serial != NULL) { /* more than one emulator listed */
- free(tmp);
- return -2;
- }
- serial = p;
- }
-
- p = q;
- }
- free(tmp);
-
- if (serial == NULL)
- return -1; /* no emulator found */
- }
- else {
- if (memcmp(serial, LOCAL_CLIENT_PREFIX, sizeof(LOCAL_CLIENT_PREFIX)-1) != 0)
- return -1; /* not an emulator */
- }
-
- serial += sizeof(LOCAL_CLIENT_PREFIX)-1;
- port = strtol(serial, NULL, 10);
- return port;
-}
-
-static char __adb_error[256] = { 0 };
-
-const char *adb_error(void)
-{
- return __adb_error;
-}
-
-static int switch_socket_transport(int fd)
-{
- char service[64];
- char tmp[5];
- int len;
-
- if (__adb_serial)
- snprintf(service, sizeof service, "host:transport:%s", __adb_serial);
- else {
+static int switch_socket_transport(int fd, std::string* error) {
+ std::string service;
+ if (__adb_serial) {
+ service += "host:transport:";
+ service += __adb_serial;
+ } else {
const char* transport_type = "???";
-
- switch (__adb_transport) {
- case kTransportUsb:
- transport_type = "transport-usb";
- break;
- case kTransportLocal:
- transport_type = "transport-local";
- break;
- case kTransportAny:
- transport_type = "transport-any";
- break;
- case kTransportHost:
- // no switch necessary
- return 0;
- break;
+ switch (__adb_transport) {
+ case kTransportUsb:
+ transport_type = "transport-usb";
+ break;
+ case kTransportLocal:
+ transport_type = "transport-local";
+ break;
+ case kTransportAny:
+ transport_type = "transport-any";
+ break;
+ case kTransportHost:
+ // no switch necessary
+ return 0;
}
-
- snprintf(service, sizeof service, "host:%s", transport_type);
+ service += "host:";
+ service += transport_type;
}
- len = strlen(service);
- snprintf(tmp, sizeof tmp, "%04x", len);
- if(!WriteFdExactly(fd, tmp, 4) || !WriteFdExactly(fd, service, len)) {
- strcpy(__adb_error, "write failure during connection");
+ if (!SendProtocolString(fd, service)) {
+ *error = perror_str("write failure during connection");
adb_close(fd);
return -1;
}
D("Switch transport in progress\n");
- if(adb_status(fd)) {
+ if (!adb_status(fd, error)) {
adb_close(fd);
- D("Switch transport failed\n");
+ D("Switch transport failed: %s\n", error->c_str());
return -1;
}
D("Switch transport success\n");
return 0;
}
-int adb_status(int fd)
-{
- unsigned char buf[5];
- unsigned len;
-
- if(!ReadFdExactly(fd, buf, 4)) {
- strcpy(__adb_error, "protocol fault (no status)");
- return -1;
+bool adb_status(int fd, std::string* error) {
+ char buf[5];
+ if (!ReadFdExactly(fd, buf, 4)) {
+ *error = perror_str("protocol fault (couldn't read status)");
+ return false;
}
- if(!memcmp(buf, "OKAY", 4)) {
- return 0;
+ if (!memcmp(buf, "OKAY", 4)) {
+ return true;
}
- if(memcmp(buf, "FAIL", 4)) {
- sprintf(__adb_error,
- "protocol fault (status %02x %02x %02x %02x?!)",
- buf[0], buf[1], buf[2], buf[3]);
- return -1;
+ if (memcmp(buf, "FAIL", 4)) {
+ *error = android::base::StringPrintf("protocol fault (status %02x %02x %02x %02x?!)",
+ buf[0], buf[1], buf[2], buf[3]);
+ return false;
}
- if(!ReadFdExactly(fd, buf, 4)) {
- strcpy(__adb_error, "protocol fault (status len)");
- return -1;
- }
- buf[4] = 0;
- len = strtoul((char*)buf, 0, 16);
- if(len > 255) len = 255;
- if(!ReadFdExactly(fd, __adb_error, len)) {
- strcpy(__adb_error, "protocol fault (status read)");
- return -1;
- }
- __adb_error[len] = 0;
- return -1;
+ ReadProtocolString(fd, error, error);
+ return false;
}
-int _adb_connect(const char *service)
-{
- char tmp[5];
- int len;
- int fd;
-
- D("_adb_connect: %s\n", service);
- len = strlen(service);
- if((len < 1) || (len > 1024)) {
- strcpy(__adb_error, "service name too long");
+int _adb_connect(const std::string& service, std::string* error) {
+ D("_adb_connect: %s\n", service.c_str());
+ if (service.empty() || service.size() > 1024) {
+ *error = android::base::StringPrintf("bad service name length (%d)",
+ static_cast<int>(service.size()));
return -1;
}
- snprintf(tmp, sizeof tmp, "%04x", len);
- if (__adb_server_name)
+ int fd;
+ if (__adb_server_name) {
fd = socket_network_client(__adb_server_name, __adb_server_port, SOCK_STREAM);
- else
+ } else {
fd = socket_loopback_client(__adb_server_port, SOCK_STREAM);
-
- if(fd < 0) {
- strcpy(__adb_error, "cannot connect to daemon");
+ }
+ if (fd < 0) {
+ *error = perror_str("cannot connect to daemon");
return -2;
}
- if (memcmp(service,"host",4) != 0 && switch_socket_transport(fd)) {
+ if (memcmp(&service[0],"host",4) != 0 && switch_socket_transport(fd, error)) {
return -1;
}
- if(!WriteFdExactly(fd, tmp, 4) || !WriteFdExactly(fd, service, len)) {
- strcpy(__adb_error, "write failure during connection");
+ if(!SendProtocolString(fd, service)) {
+ *error = perror_str("write failure during connection");
adb_close(fd);
return -1;
}
- if(adb_status(fd)) {
+ if (!adb_status(fd, error)) {
adb_close(fd);
return -1;
}
@@ -232,20 +180,19 @@
return fd;
}
-int adb_connect(const char *service)
-{
+int adb_connect(const std::string& service, std::string* error) {
// first query the adb server's version
- int fd = _adb_connect("host:version");
+ int fd = _adb_connect("host:version", error);
- D("adb_connect: service %s\n", service);
- if(fd == -2 && __adb_server_name) {
+ D("adb_connect: service %s\n", service.c_str());
+ if (fd == -2 && __adb_server_name) {
fprintf(stderr,"** Cannot start server on remote host\n");
return fd;
- } else if(fd == -2) {
+ } else if (fd == -2) {
fprintf(stdout,"* daemon not running. starting it now on port %d *\n",
__adb_server_port);
start_server:
- if(launch_server(__adb_server_port)) {
+ if (launch_server(__adb_server_port)) {
fprintf(stderr,"* failed to start daemon *\n");
return -1;
} else {
@@ -256,31 +203,31 @@
// fall through to _adb_connect
} else {
// if server was running, check its version to make sure it is not out of date
- char buf[100];
- size_t n;
int version = ADB_SERVER_VERSION - 1;
// if we have a file descriptor, then parse version result
- if(fd >= 0) {
- if(!ReadFdExactly(fd, buf, 4)) goto error;
+ if (fd >= 0) {
+ std::string version_string;
+ if (!ReadProtocolString(fd, &version_string, error)) {
+ goto error;
+ }
- buf[4] = 0;
- n = strtoul(buf, 0, 16);
- if(n > sizeof(buf)) goto error;
- if(!ReadFdExactly(fd, buf, n)) goto error;
adb_close(fd);
- if (sscanf(buf, "%04x", &version) != 1) goto error;
+ if (sscanf(&version_string[0], "%04x", &version) != 1) {
+ goto error;
+ }
} else {
// if fd is -1, then check for "unknown host service",
// which would indicate a version of adb that does not support the version command
- if (strcmp(__adb_error, "unknown host service") != 0)
+ if (*error == "unknown host service") {
return fd;
+ }
}
- if(version != ADB_SERVER_VERSION) {
+ if (version != ADB_SERVER_VERSION) {
printf("adb server is out of date. killing...\n");
- fd = _adb_connect("host:kill");
+ fd = _adb_connect("host:kill", error);
adb_close(fd);
/* XXX can we better detect its death? */
@@ -290,12 +237,13 @@
}
// if the command is start-server, we are done.
- if (!strcmp(service, "host:start-server"))
+ if (service == "host:start-server") {
return 0;
+ }
- fd = _adb_connect(service);
- if(fd == -1) {
- D("_adb_connect error: %s", __adb_error);
+ fd = _adb_connect(service, error);
+ if (fd == -1) {
+ D("_adb_connect error: %s", error->c_str());
} else if(fd == -2) {
fprintf(stderr,"** daemon still not running\n");
}
@@ -308,15 +256,14 @@
}
-int adb_command(const char *service)
-{
- int fd = adb_connect(service);
- if(fd < 0) {
- fprintf(stderr, "error: %s\n", adb_error());
+int adb_command(const std::string& service, std::string* error) {
+ int fd = adb_connect(service, error);
+ if (fd < 0) {
+ fprintf(stderr, "error: %s\n", error->c_str());
return -1;
}
- if(adb_status(fd)) {
+ if (!adb_status(fd, error)) {
adb_close(fd);
return -1;
}
@@ -324,39 +271,18 @@
return 0;
}
-char *adb_query(const char *service)
-{
- char buf[5];
- unsigned long n;
- char* tmp;
-
- D("adb_query: %s\n", service);
- int fd = adb_connect(service);
- if(fd < 0) {
- fprintf(stderr,"error: %s\n", __adb_error);
+bool adb_query(const std::string& service, std::string* result, std::string* error) {
+ D("adb_query: %s\n", service.c_str());
+ int fd = adb_connect(service, error);
+ if (fd < 0) {
+ fprintf(stderr,"error: %s\n", error->c_str());
return 0;
}
- if(!ReadFdExactly(fd, buf, 4)) goto oops;
-
- buf[4] = 0;
- n = strtoul(buf, 0, 16);
- if(n >= 0xffff) {
- strcpy(__adb_error, "reply is too long (>= 64kB)");
- goto oops;
- }
-
- tmp = reinterpret_cast<char*>(malloc(n + 1));
- if(tmp == 0) goto oops;
-
- if(!ReadFdExactly(fd, tmp, n) == 0) {
- tmp[n] = 0;
+ result->clear();
+ if (!ReadProtocolString(fd, result, error)) {
adb_close(fd);
- return tmp;
+ return false;
}
- free(tmp);
-
-oops:
- adb_close(fd);
- return 0;
+ return true;
}
diff --git a/adb/adb_client.h b/adb/adb_client.h
index 9af176f..9895c49 100644
--- a/adb/adb_client.h
+++ b/adb/adb_client.h
@@ -1,65 +1,55 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#ifndef _ADB_CLIENT_H_
#define _ADB_CLIENT_H_
#include "adb.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include <string>
-/* connect to adb, connect to the named service, and return
-** a valid fd for interacting with that service upon success
-** or a negative number on failure
-*/
-int adb_connect(const char *service);
-int _adb_connect(const char *service);
+// Connect to adb, connect to the named service, and return a valid fd for
+// interacting with that service upon success or a negative number on failure.
+int adb_connect(const std::string& service, std::string* error);
+int _adb_connect(const std::string& service, std::string* error);
-/* connect to adb, connect to the named service, return 0 if
-** the connection succeeded AND the service returned OKAY
-*/
-int adb_command(const char *service);
+// Connect to adb, connect to the named service, return 0 if the connection
+// succeeded AND the service returned OKAY.
+int adb_command(const std::string& service, std::string* error);
-/* connect to adb, connect to the named service, return
-** a malloc'd string of its response upon success or NULL
-** on failure.
-*/
-char *adb_query(const char *service);
+// Connects to the named adb service and fills 'result' with the response.
+// Returns true on success; returns false and fills 'error' on failure.
+bool adb_query(const std::string& service, std::string* result, std::string* error);
-/* Set the preferred transport to connect to.
-*/
-void adb_set_transport(transport_type type, const char* serial);
+// Set the preferred transport to connect to.
+void adb_set_transport(TransportType type, const char* serial);
-/* Set TCP specifics of the transport to use
-*/
+// Set TCP specifics of the transport to use.
void adb_set_tcp_specifics(int server_port);
-/* Set TCP Hostname of the transport to use
-*/
+// Set TCP Hostname of the transport to use.
void adb_set_tcp_name(const char* hostname);
-/* Return the console port of the currently connected emulator (if any)
- * of -1 if there is no emulator, and -2 if there is more than one.
- * assumes adb_set_transport() was alled previously...
- */
-int adb_get_emulator_console_port(void);
+// Send commands to the current emulator instance. Will fail if there is not
+// exactly one emulator connected (or if you use -s <serial> with a <serial>
+// that does not designate an emulator).
+int adb_send_emulator_command(int argc, const char** argv, const char* serial);
-/* send commands to the current emulator instance. will fail if there
- * is zero, or more than one emulator connected (or if you use -s <serial>
- * with a <serial> that does not designate an emulator)
- */
-int adb_send_emulator_command(int argc, const char** argv);
-
-/* return verbose error string from last operation */
-const char *adb_error(void);
-
-/* read a standard adb status response (OKAY|FAIL) and
-** return 0 in the event of OKAY, -1 in the event of FAIL
-** or protocol error
-*/
-int adb_status(int fd);
-
-#ifdef __cplusplus
-}
-#endif
+// Reads a standard adb status response (OKAY|FAIL) and returns true in the
+// event of OKAY, false in the event of FAIL or protocol error.
+bool adb_status(int fd, std::string* error);
#endif
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index 4dd9f4d..5ae6ec3 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -14,22 +14,39 @@
* limitations under the License.
*/
-#define TRACE_TAG TRACE_RWX
+#define TRACE_TAG TRACE_RWX
-#include "sysdeps.h"
#include "adb_io.h"
#include <unistd.h>
+#include <base/stringprintf.h>
+
#include "adb_trace.h"
-#include "transport.h"
+#include "adb_utils.h"
+#include "sysdeps.h"
+
+bool SendProtocolString(int fd, const std::string& s) {
+ int length = s.size();
+ if (length > 0xffff) {
+ length = 0xffff;
+ }
+
+ return WriteFdFmt(fd, "%04x", length) && WriteFdExactly(fd, s);
+}
+
+bool SendOkay(int fd) {
+ return WriteFdExactly(fd, "OKAY", 4);
+}
+
+bool SendFail(int fd, const std::string& reason) {
+ return WriteFdExactly(fd, "FAIL", 4) && SendProtocolString(fd, reason);
+}
bool ReadFdExactly(int fd, void* buf, size_t len) {
char* p = reinterpret_cast<char*>(buf);
-#if ADB_TRACE
size_t len0 = len;
-#endif
D("readx: fd=%d wanted=%zu\n", fd, len);
while (len > 0) {
@@ -47,12 +64,10 @@
}
}
-#if ADB_TRACE
D("readx: fd=%d wanted=%zu got=%zu\n", fd, len0, len0 - len);
if (ADB_TRACING) {
dump_hex(reinterpret_cast<const unsigned char*>(buf), len0);
}
-#endif
return true;
}
@@ -61,12 +76,10 @@
const char* p = reinterpret_cast<const char*>(buf);
int r;
-#if ADB_TRACE
D("writex: fd=%d len=%d: ", fd, (int)len);
if (ADB_TRACING) {
dump_hex(reinterpret_cast<const unsigned char*>(buf), len);
}
-#endif
while (len > 0) {
r = adb_write(fd, p, len);
@@ -79,6 +92,8 @@
D("writex: fd=%d disconnected\n", fd);
errno = 0;
return false;
+ } else {
+ return false;
}
} else {
len -= r;
@@ -88,6 +103,21 @@
return true;
}
-bool WriteStringFully(int fd, const char* str) {
+bool WriteFdExactly(int fd, const char* str) {
return WriteFdExactly(fd, str, strlen(str));
}
+
+bool WriteFdExactly(int fd, const std::string& str) {
+ return WriteFdExactly(fd, str.c_str(), str.size());
+}
+
+bool WriteFdFmt(int fd, const char* fmt, ...) {
+ std::string str;
+
+ va_list ap;
+ va_start(ap, fmt);
+ android::base::StringAppendV(&str, fmt, ap);
+ va_end(ap);
+
+ return WriteFdExactly(fd, str);
+}
diff --git a/adb/adb_io.h b/adb/adb_io.h
index 7d09e7b..8d50a6d 100644
--- a/adb/adb_io.h
+++ b/adb/adb_io.h
@@ -17,12 +17,18 @@
#ifndef ADB_IO_H
#define ADB_IO_H
-#include <stdbool.h>
#include <sys/types.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include <string>
+
+// Sends the protocol "OKAY" message.
+bool SendOkay(int fd);
+
+// Sends the protocol "FAIL" message, with the given failure reason.
+bool SendFail(int fd, const std::string& reason);
+
+// Writes a protocol-format string; a four hex digit length followed by the string data.
+bool SendProtocolString(int fd, const std::string& s);
/*
* Reads exactly len bytes from fd into buf.
@@ -41,13 +47,13 @@
* completed. If the other end of the fd (such as in a socket, pipe, or fifo),
* is closed, errno will be set to 0.
*/
-bool WriteFdExactly(int fd, const void *buf, size_t len);
+bool WriteFdExactly(int fd, const void* buf, size_t len);
-/* Same as WriteFdExactly, but with an implicit len = strlen(buf). */
-bool WriteStringFully(int fd, const char* str);
+// Same as above, but for strings.
+bool WriteFdExactly(int fd, const char* s);
+bool WriteFdExactly(int fd, const std::string& s);
-#ifdef __cplusplus
-}
-#endif
+// Same as above, but formats the string to send.
+bool WriteFdFmt(int fd, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
#endif /* ADB_IO_H */
diff --git a/adb/adb_io_test.cpp b/adb/adb_io_test.cpp
index 0c69bc9..8fd5cbf 100644
--- a/adb/adb_io_test.cpp
+++ b/adb/adb_io_test.cpp
@@ -18,8 +18,11 @@
#include <gtest/gtest.h>
+#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <unistd.h>
#include <string>
@@ -127,16 +130,38 @@
EXPECT_EQ(expected, s);
}
-TEST(io, WriteStringFully) {
+TEST(io, WriteFdExactly_ENOSPC) {
+ int fd = open("/dev/full", O_WRONLY);
+ ASSERT_NE(-1, fd);
+
+ char buf[] = "foo";
+ ASSERT_FALSE(WriteFdExactly(fd, buf, sizeof(buf)));
+ ASSERT_EQ(ENOSPC, errno);
+}
+
+TEST(io, WriteFdExactly_string) {
const char str[] = "Foobar";
TemporaryFile tf;
ASSERT_NE(-1, tf.fd);
// Test writing a partial string to the file.
- ASSERT_TRUE(WriteStringFully(tf.fd, str)) << strerror(errno);
+ ASSERT_TRUE(WriteFdExactly(tf.fd, str)) << strerror(errno);
ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
std::string s;
ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
EXPECT_STREQ(str, s.c_str());
}
+
+TEST(io, WriteFdFmt) {
+ TemporaryFile tf;
+ ASSERT_NE(-1, tf.fd);
+
+ // Test writing a partial string to the file.
+ ASSERT_TRUE(WriteFdFmt(tf.fd, "Foo%s%d", "bar", 123)) << strerror(errno);
+ ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+
+ std::string s;
+ ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
+ EXPECT_STREQ("Foobar123", s.c_str());
+}
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index 84b9c64..cf193ab 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -19,6 +19,8 @@
#include <stdio.h>
#include <stdlib.h>
+#include <base/stringprintf.h>
+
#include "sysdeps.h"
#include "transport.h"
@@ -143,64 +145,31 @@
return -1;
}
-// Write a single line describing a listener to a user-provided buffer.
-// Appends a trailing zero, even in case of truncation, but the function
-// returns the full line length.
-// If |buffer| is NULL, does not write but returns required size.
-static int format_listener(alistener* l, char* buffer, size_t buffer_len) {
- // Format is simply:
- //
- // <device-serial> " " <local-name> " " <remote-name> "\n"
- //
- int local_len = strlen(l->local_name);
- int connect_len = strlen(l->connect_to);
- int serial_len = strlen(l->transport->serial);
-
- if (buffer != NULL) {
- snprintf(buffer, buffer_len, "%s %s %s\n",
- l->transport->serial, l->local_name, l->connect_to);
- }
- // NOTE: snprintf() on Windows returns -1 in case of truncation, so
- // return the computed line length instead.
- return local_len + connect_len + serial_len + 3;
-}
-
-// Write the list of current listeners (network redirections) into a
-// user-provided buffer. Appends a trailing zero, even in case of
-// trunctaion, but return the full size in bytes.
-// If |buffer| is NULL, does not write but returns required size.
-int format_listeners(char* buf, size_t buflen)
-{
- alistener* l;
- int result = 0;
- for (l = listener_list.next; l != &listener_list; l = l->next) {
+// Write the list of current listeners (network redirections) into a string.
+std::string format_listeners() {
+ std::string result;
+ for (alistener* l = listener_list.next; l != &listener_list; l = l->next) {
// Ignore special listeners like those for *smartsocket*
- if (l->connect_to[0] == '*')
- continue;
- int len = format_listener(l, buf, buflen);
- // Ensure there is space for the trailing zero.
- result += len;
- if (buf != NULL) {
- buf += len;
- buflen -= len;
- if (buflen <= 0)
- break;
+ if (l->connect_to[0] == '*') {
+ continue;
}
+ // <device-serial> " " <local-name> " " <remote-name> "\n"
+ android::base::StringAppendF(&result, "%s %s %s\n",
+ l->transport->serial, l->local_name, l->connect_to);
}
return result;
}
-int remove_listener(const char *local_name, atransport* transport)
-{
+InstallStatus remove_listener(const char *local_name, atransport* transport) {
alistener *l;
for (l = listener_list.next; l != &listener_list; l = l->next) {
if (!strcmp(local_name, l->local_name)) {
listener_disconnect(l, l->transport);
- return 0;
+ return INSTALL_STATUS_OK;
}
}
- return -1;
+ return INSTALL_STATUS_LISTENER_NOT_FOUND;
}
void remove_all_listeners(void)
@@ -215,13 +184,13 @@
}
}
-install_status_t install_listener(const char *local_name,
+InstallStatus install_listener(const std::string& local_name,
const char *connect_to,
atransport* transport,
int no_rebind)
{
for (alistener* l = listener_list.next; l != &listener_list; l = l->next) {
- if (strcmp(local_name, l->local_name) == 0) {
+ if (local_name == l->local_name) {
char* cto;
/* can't repurpose a smartsocket */
@@ -256,7 +225,7 @@
goto nomem;
}
- listener->local_name = strdup(local_name);
+ listener->local_name = strdup(local_name.c_str());
if (listener->local_name == nullptr) {
goto nomem;
}
@@ -266,12 +235,12 @@
goto nomem;
}
- listener->fd = local_name_to_fd(local_name);
+ listener->fd = local_name_to_fd(listener->local_name);
if (listener->fd < 0) {
+ printf("cannot bind '%s': %s\n", listener->local_name, strerror(errno));
free(listener->local_name);
free(listener->connect_to);
free(listener);
- printf("cannot bind '%s'\n", local_name);
return INSTALL_STATUS_CANNOT_BIND;
}
diff --git a/adb/adb_listeners.h b/adb/adb_listeners.h
index 14fdcd6..9a7ded1 100644
--- a/adb/adb_listeners.h
+++ b/adb/adb_listeners.h
@@ -19,17 +19,16 @@
#include "adb.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include <string>
// error/status codes for install_listener.
-typedef enum {
+enum InstallStatus {
INSTALL_STATUS_OK = 0,
INSTALL_STATUS_INTERNAL_ERROR = -1,
INSTALL_STATUS_CANNOT_BIND = -2,
INSTALL_STATUS_CANNOT_REBIND = -3,
-} install_status_t;
+ INSTALL_STATUS_LISTENER_NOT_FOUND = -4,
+};
extern alistener listener_list;
@@ -37,18 +36,14 @@
void listener_event_func(int _fd, unsigned ev, void *_l);
void ss_listener_event_func(int _fd, unsigned ev, void *_l);
-install_status_t install_listener(const char *local_name,
- const char *connect_to,
- atransport* transport,
- int no_rebind);
+InstallStatus install_listener(const std::string& local_name,
+ const char* connect_to,
+ atransport* transport,
+ int no_rebind);
-int format_listeners(char* buf, size_t buflen);
+std::string format_listeners();
-int remove_listener(const char *local_name, atransport* transport);
+InstallStatus remove_listener(const char* local_name, atransport* transport);
void remove_all_listeners(void);
-#ifdef __cplusplus
-}
-#endif
-
#endif /* __ADB_LISTENERS_H */
diff --git a/adb/adb_main.cpp b/adb/adb_main.cpp
deleted file mode 100644
index 1d9cc3b..0000000
--- a/adb/adb_main.cpp
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define TRACE_TAG TRACE_ADB
-
-#include <errno.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "sysdeps.h"
-
-#include "adb.h"
-#include "adb_auth.h"
-#include "adb_listeners.h"
-#include "transport.h"
-
-#if !ADB_HOST
-#include <getopt.h>
-#include <sys/prctl.h>
-
-#include "cutils/properties.h"
-#include "private/android_filesystem_config.h"
-#include "selinux/selinux.h"
-
-#include "qemu_tracing.h"
-#endif
-
-static void adb_cleanup(void)
-{
- usb_cleanup();
-}
-
-#if defined(_WIN32)
-static BOOL WINAPI ctrlc_handler(DWORD type)
-{
- exit(STATUS_CONTROL_C_EXIT);
- return TRUE;
-}
-#endif
-
-#if ADB_HOST
-#ifdef WORKAROUND_BUG6558362
-#include <sched.h>
-#define AFFINITY_ENVVAR "ADB_CPU_AFFINITY_BUG6558362"
-void adb_set_affinity(void)
-{
- cpu_set_t cpu_set;
- const char* cpunum_str = getenv(AFFINITY_ENVVAR);
- char* strtol_res;
- int cpu_num;
-
- if (!cpunum_str || !*cpunum_str)
- return;
- cpu_num = strtol(cpunum_str, &strtol_res, 0);
- if (*strtol_res != '\0')
- fatal("bad number (%s) in env var %s. Expecting 0..n.\n", cpunum_str, AFFINITY_ENVVAR);
-
- sched_getaffinity(0, sizeof(cpu_set), &cpu_set);
- D("orig cpu_set[0]=0x%08lx\n", cpu_set.__bits[0]);
- CPU_ZERO(&cpu_set);
- CPU_SET(cpu_num, &cpu_set);
- sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
- sched_getaffinity(0, sizeof(cpu_set), &cpu_set);
- D("new cpu_set[0]=0x%08lx\n", cpu_set.__bits[0]);
-}
-#endif
-#else /* ADB_HOST */
-static const char *root_seclabel = NULL;
-
-static void drop_capabilities_bounding_set_if_needed() {
-#ifdef ALLOW_ADBD_ROOT
- char value[PROPERTY_VALUE_MAX];
- property_get("ro.debuggable", value, "");
- if (strcmp(value, "1") == 0) {
- return;
- }
-#endif
- int i;
- for (i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
- if (i == CAP_SETUID || i == CAP_SETGID) {
- // CAP_SETUID CAP_SETGID needed by /system/bin/run-as
- continue;
- }
- int err = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
-
- // Some kernels don't have file capabilities compiled in, and
- // prctl(PR_CAPBSET_DROP) returns EINVAL. Don't automatically
- // die when we see such misconfigured kernels.
- if ((err < 0) && (errno != EINVAL)) {
- exit(1);
- }
- }
-}
-
-static bool should_drop_privileges() {
-#if defined(ALLOW_ADBD_ROOT)
- char value[PROPERTY_VALUE_MAX];
-
- // The properties that affect `adb root` and `adb unroot` are ro.secure and
- // ro.debuggable. In this context the names don't make the expected behavior
- // particularly obvious.
- //
- // ro.debuggable:
- // Allowed to become root, but not necessarily the default. Set to 1 on
- // eng and userdebug builds.
- //
- // ro.secure:
- // Drop privileges by default. Set to 1 on userdebug and user builds.
- property_get("ro.secure", value, "1");
- bool ro_secure = (strcmp(value, "1") == 0);
-
- property_get("ro.debuggable", value, "");
- bool ro_debuggable = (strcmp(value, "1") == 0);
-
- // Drop privileges if ro.secure is set...
- bool drop = ro_secure;
-
- property_get("service.adb.root", value, "");
- bool adb_root = (strcmp(value, "1") == 0);
- bool adb_unroot = (strcmp(value, "0") == 0);
-
- // ...except "adb root" lets you keep privileges in a debuggable build.
- if (ro_debuggable && adb_root) {
- drop = false;
- }
-
- // ...and "adb unroot" lets you explicitly drop privileges.
- if (adb_unroot) {
- drop = true;
- }
-
- return drop;
-#else
- return true; // "adb root" not allowed, always drop privileges.
-#endif /* ALLOW_ADBD_ROOT */
-}
-
-void start_device_log(void)
-{
- int fd;
- char path[PATH_MAX];
- struct tm now;
- time_t t;
- char value[PROPERTY_VALUE_MAX];
-
- // read the trace mask from persistent property persist.adb.trace_mask
- // give up if the property is not set or cannot be parsed
- property_get("persist.adb.trace_mask", value, "");
- if (sscanf(value, "%x", &adb_trace_mask) != 1)
- return;
-
- adb_mkdir("/data/adb", 0775);
- tzset();
- time(&t);
- localtime_r(&t, &now);
- strftime(path, sizeof(path),
- "/data/adb/adb-%Y-%m-%d-%H-%M-%S.txt",
- &now);
- fd = unix_open(path, O_WRONLY | O_CREAT | O_TRUNC, 0640);
- if (fd < 0)
- return;
-
- // redirect stdout and stderr to the log file
- dup2(fd, 1);
- dup2(fd, 2);
- fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid());
- adb_close(fd);
-
- fd = unix_open("/dev/null", O_RDONLY);
- dup2(fd, 0);
- adb_close(fd);
-}
-#endif /* ADB_HOST */
-
-/* Constructs a local name of form tcp:port.
- * target_str points to the target string, it's content will be overwritten.
- * target_size is the capacity of the target string.
- * server_port is the port number to use for the local name.
- */
-void build_local_name(char* target_str, size_t target_size, int server_port)
-{
- snprintf(target_str, target_size, "tcp:%d", server_port);
-}
-
-void start_logging(void)
-{
-#if defined(_WIN32)
- char temp[ MAX_PATH ];
- FILE* fnul;
- FILE* flog;
-
- GetTempPath( sizeof(temp) - 8, temp );
- strcat( temp, "adb.log" );
-
- /* Win32 specific redirections */
- fnul = fopen( "NUL", "rt" );
- if (fnul != NULL)
- stdin[0] = fnul[0];
-
- flog = fopen( temp, "at" );
- if (flog == NULL)
- flog = fnul;
-
- setvbuf( flog, NULL, _IONBF, 0 );
-
- stdout[0] = flog[0];
- stderr[0] = flog[0];
- fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid());
-#else
- int fd;
-
- fd = unix_open("/dev/null", O_RDONLY);
- dup2(fd, 0);
- adb_close(fd);
-
- fd = unix_open("/tmp/adb.log", O_WRONLY | O_CREAT | O_APPEND, 0640);
- if(fd < 0) {
- fd = unix_open("/dev/null", O_WRONLY);
- }
- dup2(fd, 1);
- dup2(fd, 2);
- adb_close(fd);
- fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid());
-#endif
-}
-
-int adb_main(int is_daemon, int server_port)
-{
-#if !ADB_HOST
- int port;
- char value[PROPERTY_VALUE_MAX];
-
- umask(000);
-#endif
-
- atexit(adb_cleanup);
-#if defined(_WIN32)
- SetConsoleCtrlHandler( ctrlc_handler, TRUE );
-#else
- // No SIGCHLD. Let the service subproc handle its children.
- signal(SIGPIPE, SIG_IGN);
-#endif
-
- init_transport_registration();
-
-#if ADB_HOST
- HOST = 1;
-
-#ifdef WORKAROUND_BUG6558362
- if(is_daemon) adb_set_affinity();
-#endif
- usb_init();
- local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
- adb_auth_init();
-
- char local_name[30];
- build_local_name(local_name, sizeof(local_name), server_port);
- if(install_listener(local_name, "*smartsocket*", NULL, 0)) {
- exit(1);
- }
-#else
- // We need to call this even if auth isn't enabled because the file
- // descriptor will always be open.
- adbd_cloexec_auth_socket();
-
- property_get("ro.adb.secure", value, "0");
- auth_enabled = !strcmp(value, "1");
- if (auth_enabled)
- adbd_auth_init();
-
- // Our external storage path may be different than apps, since
- // we aren't able to bind mount after dropping root.
- const char* adb_external_storage = getenv("ADB_EXTERNAL_STORAGE");
- if (NULL != adb_external_storage) {
- setenv("EXTERNAL_STORAGE", adb_external_storage, 1);
- } else {
- D("Warning: ADB_EXTERNAL_STORAGE is not set. Leaving EXTERNAL_STORAGE"
- " unchanged.\n");
- }
-
- /* add extra groups:
- ** AID_ADB to access the USB driver
- ** AID_LOG to read system logs (adb logcat)
- ** AID_INPUT to diagnose input issues (getevent)
- ** AID_INET to diagnose network issues (ping)
- ** AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
- ** AID_SDCARD_R to allow reading from the SD card
- ** AID_SDCARD_RW to allow writing to the SD card
- ** AID_NET_BW_STATS to read out qtaguid statistics
- */
- gid_t groups[] = { AID_ADB, AID_LOG, AID_INPUT, AID_INET, AID_NET_BT,
- AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW,
- AID_NET_BW_STATS };
- if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
- exit(1);
- }
-
- /* don't listen on a port (default 5037) if running in secure mode */
- /* don't run as root if we are running in secure mode */
- if (should_drop_privileges()) {
- drop_capabilities_bounding_set_if_needed();
-
- /* then switch user and group to "shell" */
- if (setgid(AID_SHELL) != 0) {
- exit(1);
- }
- if (setuid(AID_SHELL) != 0) {
- exit(1);
- }
-
- D("Local port disabled\n");
- } else {
- char local_name[30];
- if ((root_seclabel != NULL) && (is_selinux_enabled() > 0)) {
- // b/12587913: fix setcon to allow const pointers
- if (setcon((char *)root_seclabel) < 0) {
- exit(1);
- }
- }
- build_local_name(local_name, sizeof(local_name), server_port);
- if(install_listener(local_name, "*smartsocket*", NULL, 0)) {
- exit(1);
- }
- }
-
- int usb = 0;
- if (access(USB_ADB_PATH, F_OK) == 0 || access(USB_FFS_ADB_EP0, F_OK) == 0) {
- // listen on USB
- usb_init();
- usb = 1;
- }
-
- // If one of these properties is set, also listen on that port
- // If one of the properties isn't set and we couldn't listen on usb,
- // listen on the default port.
- property_get("service.adb.tcp.port", value, "");
- if (!value[0]) {
- property_get("persist.adb.tcp.port", value, "");
- }
- if (sscanf(value, "%d", &port) == 1 && port > 0) {
- printf("using port=%d\n", port);
- // listen on TCP port specified by service.adb.tcp.port property
- local_init(port);
- } else if (!usb) {
- // listen on default port
- local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
- }
-
- D("adb_main(): pre init_jdwp()\n");
- init_jdwp();
- D("adb_main(): post init_jdwp()\n");
-#endif
-
- if (is_daemon)
- {
- // inform our parent that we are up and running.
-#if defined(_WIN32)
- DWORD count;
- WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), "OK\n", 3, &count, NULL );
-#else
- fprintf(stderr, "OK\n");
-#endif
- start_logging();
- }
- D("Event loop starting\n");
-
- fdevent_loop();
-
- usb_cleanup();
-
- return 0;
-}
-
-int main(int argc, char **argv)
-{
-#if ADB_HOST
- adb_sysdeps_init();
- adb_trace_init();
- D("Handling commandline()\n");
- return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
-#else
- /* If adbd runs inside the emulator this will enable adb tracing via
- * adb-debug qemud service in the emulator. */
- adb_qemu_trace_init();
- while(1) {
- int c;
- int option_index = 0;
- static struct option opts[] = {
- {"root_seclabel", required_argument, 0, 's' },
- {"device_banner", required_argument, 0, 'b' }
- };
- c = getopt_long(argc, argv, "", opts, &option_index);
- if (c == -1)
- break;
- switch (c) {
- case 's':
- root_seclabel = optarg;
- break;
- case 'b':
- adb_device_banner = optarg;
- break;
- default:
- break;
- }
- }
-
- start_device_log();
- D("Handling main()\n");
- return adb_main(0, DEFAULT_ADB_PORT);
-#endif
-}
diff --git a/adb/adb_trace.h b/adb/adb_trace.h
index ef5dc24..63d4151 100644
--- a/adb/adb_trace.h
+++ b/adb/adb_trace.h
@@ -23,18 +23,11 @@
#include <stdio.h>
#endif
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* define ADB_TRACE to 1 to enable tracing support, or 0 to disable it */
-#define ADB_TRACE 1
-
/* IMPORTANT: if you change the following list, don't
* forget to update the corresponding 'tags' table in
* the adb_trace_init() function implemented in adb.c
*/
-typedef enum {
+enum AdbTrace {
TRACE_ADB = 0, /* 0x001 */
TRACE_SOCKETS,
TRACE_PACKETS,
@@ -47,9 +40,7 @@
TRACE_SERVICES,
TRACE_AUTH,
TRACE_FDEVENT,
-} AdbTrace;
-
-#if ADB_TRACE
+} ;
#if !ADB_HOST
/*
@@ -101,19 +92,6 @@
errno = save_errno; \
} \
} while (0)
-# define DD(...) \
- do { \
- int save_errno = errno; \
- adb_mutex_lock(&D_lock); \
- fprintf(stderr, "%16s: %5d:%5lu | ", \
- __FUNCTION__, \
- getpid(), adb_thread_id()); \
- errno = save_errno; \
- fprintf(stderr, __VA_ARGS__ ); \
- fflush(stderr); \
- adb_mutex_unlock(&D_lock); \
- errno = save_errno; \
- } while (0)
#else
# define D(...) \
do { \
@@ -133,23 +111,6 @@
__VA_ARGS__ ); \
} \
} while (0)
-# define DD(...) \
- do { \
- __android_log_print( \
- ANDROID_LOG_INFO, \
- __FUNCTION__, \
- __VA_ARGS__ ); \
- } while (0)
#endif /* ADB_HOST */
-#else
-# define D(...) ((void)0)
-# define DR(...) ((void)0)
-# define DD(...) ((void)0)
-# define ADB_TRACING 0
-#endif /* ADB_TRACE */
-
-#ifdef __cplusplus
-}
-#endif
#endif /* __ADB_TRACE_H */
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
new file mode 100644
index 0000000..0ce5ece
--- /dev/null
+++ b/adb/adb_utils.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG TRACE_ADB
+
+#include "adb_utils.h"
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include <base/stringprintf.h>
+
+#include "adb_trace.h"
+#include "sysdeps.h"
+
+bool getcwd(std::string* s) {
+ char* cwd = getcwd(nullptr, 0);
+ if (cwd != nullptr) *s = cwd;
+ free(cwd);
+ return (cwd != nullptr);
+}
+
+bool directory_exists(const std::string& path) {
+ struct stat sb;
+ return lstat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode);
+}
+
+std::string escape_arg(const std::string& s) {
+ std::string result = s;
+
+ // Insert a \ before any ' in the string.
+ for (auto it = result.begin(); it != result.end(); ++it) {
+ if (*it == '\'') {
+ it = result.insert(it, '\\') + 1;
+ }
+ }
+
+ // Prefix and suffix the whole string with '.
+ result.insert(result.begin(), '\'');
+ result.push_back('\'');
+ return result;
+}
+
+void dump_hex(const void* data, size_t byte_count) {
+ byte_count = std::min(byte_count, size_t(16));
+
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
+
+ std::string line;
+ for (size_t i = 0; i < byte_count; ++i) {
+ android::base::StringAppendF(&line, "%02x", p[i]);
+ }
+ line.push_back(' ');
+
+ for (size_t i = 0; i < byte_count; ++i) {
+ int c = p[i];
+ if (c < 32 || c > 127) {
+ c = '.';
+ }
+ line.push_back(c);
+ }
+
+ DR("%s\n", line.c_str());
+}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
new file mode 100644
index 0000000..84f7d0c
--- /dev/null
+++ b/adb/adb_utils.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ADB_UTILS_H_
+#define _ADB_UTILS_H_
+
+#include <string>
+
+bool getcwd(std::string* cwd);
+bool directory_exists(const std::string& path);
+
+std::string escape_arg(const std::string& s);
+
+void dump_hex(const void* ptr, size_t byte_count);
+
+#endif
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
new file mode 100644
index 0000000..a395079
--- /dev/null
+++ b/adb/adb_utils_test.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "adb_utils.h"
+
+#include <gtest/gtest.h>
+
+TEST(adb_utils, directory_exists) {
+ ASSERT_TRUE(directory_exists("/proc"));
+ ASSERT_FALSE(directory_exists("/proc/self")); // Symbolic link.
+ ASSERT_FALSE(directory_exists("/proc/does-not-exist"));
+}
+
+TEST(adb_utils, escape_arg) {
+ ASSERT_EQ(R"('')", escape_arg(""));
+
+ ASSERT_EQ(R"('abc')", escape_arg("abc"));
+
+ ASSERT_EQ(R"(' abc')", escape_arg(" abc"));
+ ASSERT_EQ(R"('\'abc')", escape_arg("'abc"));
+ ASSERT_EQ(R"('"abc')", escape_arg("\"abc"));
+ ASSERT_EQ(R"('\abc')", escape_arg("\\abc"));
+ ASSERT_EQ(R"('(abc')", escape_arg("(abc"));
+ ASSERT_EQ(R"(')abc')", escape_arg(")abc"));
+
+ ASSERT_EQ(R"('abc abc')", escape_arg("abc abc"));
+ ASSERT_EQ(R"('abc\'abc')", escape_arg("abc'abc"));
+ ASSERT_EQ(R"('abc"abc')", escape_arg("abc\"abc"));
+ ASSERT_EQ(R"('abc\abc')", escape_arg("abc\\abc"));
+ ASSERT_EQ(R"('abc(abc')", escape_arg("abc(abc"));
+ ASSERT_EQ(R"('abc)abc')", escape_arg("abc)abc"));
+
+ ASSERT_EQ(R"('abc ')", escape_arg("abc "));
+ ASSERT_EQ(R"('abc\'')", escape_arg("abc'"));
+ ASSERT_EQ(R"('abc"')", escape_arg("abc\""));
+ ASSERT_EQ(R"('abc\')", escape_arg("abc\\"));
+ ASSERT_EQ(R"('abc(')", escape_arg("abc("));
+ ASSERT_EQ(R"('abc)')", escape_arg("abc)"));
+}
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
new file mode 100644
index 0000000..468909a
--- /dev/null
+++ b/adb/client/main.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG TRACE_ADB
+
+#include "sysdeps.h"
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+// We only build the affinity WAR code for Linux.
+#if defined(__linux__)
+#include <sched.h>
+#endif
+
+#include "base/file.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+
+#include "adb.h"
+#include "adb_auth.h"
+#include "adb_listeners.h"
+#include "transport.h"
+
+#if defined(WORKAROUND_BUG6558362) && defined(__linux__)
+static const bool kWorkaroundBug6558362 = true;
+#else
+static const bool kWorkaroundBug6558362 = false;
+#endif
+
+static void adb_workaround_affinity(void) {
+#if defined(__linux__)
+ const char affinity_env[] = "ADB_CPU_AFFINITY_BUG6558362";
+ const char* cpunum_str = getenv(affinity_env);
+ if (cpunum_str == nullptr || *cpunum_str == '\0') {
+ return;
+ }
+
+ char* strtol_res;
+ int cpu_num = strtol(cpunum_str, &strtol_res, 0);
+ if (*strtol_res != '\0') {
+ fatal("bad number (%s) in env var %s. Expecting 0..n.\n", cpunum_str,
+ affinity_env);
+ }
+
+ cpu_set_t cpu_set;
+ sched_getaffinity(0, sizeof(cpu_set), &cpu_set);
+ D("orig cpu_set[0]=0x%08lx\n", cpu_set.__bits[0]);
+
+ CPU_ZERO(&cpu_set);
+ CPU_SET(cpu_num, &cpu_set);
+ sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
+
+ sched_getaffinity(0, sizeof(cpu_set), &cpu_set);
+ D("new cpu_set[0]=0x%08lx\n", cpu_set.__bits[0]);
+#else
+ // No workaround was ever implemented for the other platforms.
+#endif
+}
+
+#if defined(_WIN32)
+static const char kNullFileName[] = "NUL";
+
+static BOOL WINAPI ctrlc_handler(DWORD type) {
+ exit(STATUS_CONTROL_C_EXIT);
+ return TRUE;
+}
+
+static std::string GetLogFilePath() {
+ const char log_name[] = "adb.log";
+ char temp_path[MAX_PATH - sizeof(log_name) + 1];
+
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx
+ DWORD nchars = GetTempPath(sizeof(temp_path), temp_path);
+ CHECK_LE(nchars, sizeof(temp_path));
+ if (nchars == 0) {
+ // TODO(danalbert): Log the error message from FormatError().
+ // Windows unfortunately has two errnos, errno and GetLastError(), so
+ // I'm not sure what to do about PLOG here. Probably better to just
+ // ignore it and add a simplified version of FormatError() for use in
+ // log messages.
+ LOG(ERROR) << "Error creating log file";
+ }
+
+ return std::string(temp_path) + log_name;
+}
+#else
+static const char kNullFileName[] = "/dev/null";
+
+static std::string GetLogFilePath() {
+ return std::string("/tmp/adb.log");
+}
+#endif
+
+static void close_stdin() {
+ int fd = unix_open(kNullFileName, O_RDONLY);
+ CHECK_NE(fd, -1);
+ dup2(fd, STDIN_FILENO);
+ adb_close(fd);
+}
+
+static void setup_daemon_logging(void) {
+ int fd = unix_open(GetLogFilePath().c_str(), O_WRONLY | O_CREAT | O_APPEND,
+ 0640);
+ if (fd == -1) {
+ fd = unix_open(kNullFileName, O_WRONLY);
+ }
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ adb_close(fd);
+ fprintf(stderr, "--- adb starting (pid %d) ---\n", getpid());
+}
+
+int adb_main(int is_daemon, int server_port) {
+ HOST = 1;
+
+#if defined(_WIN32)
+ SetConsoleCtrlHandler(ctrlc_handler, TRUE);
+#else
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ init_transport_registration();
+
+ if (kWorkaroundBug6558362 && is_daemon) {
+ adb_workaround_affinity();
+ }
+
+ usb_init();
+ local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
+ adb_auth_init();
+
+ std::string local_name = android::base::StringPrintf("tcp:%d", server_port);
+ if (install_listener(local_name, "*smartsocket*", nullptr, 0)) {
+ LOG(FATAL) << "Could not install *smartsocket* listener";
+ }
+
+ if (is_daemon) {
+ // Inform our parent that we are up and running.
+ // TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
+ // "OKAY".
+ // TODO(danalbert): Why do we use stdout for Windows?
+#if defined(_WIN32)
+ int reply_fd = STDOUT_FILENO;
+#else
+ int reply_fd = STDERR_FILENO;
+#endif
+ android::base::WriteStringToFd("OK\n", reply_fd);
+ close_stdin();
+ setup_daemon_logging();
+ }
+
+ D("Event loop starting\n");
+ fdevent_loop();
+
+ return 0;
+}
+
+int main(int argc, char** argv) {
+ adb_sysdeps_init();
+
+ android::base::InitLogging(argv);
+ adb_trace_init();
+ D("Handling commandline()\n");
+ return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
+}
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 4538b04..6caec6c 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -14,9 +14,14 @@
* limitations under the License.
*/
+#define TRACE_TAG TRACE_ADB
+
+#include "sysdeps.h"
+
#include <assert.h>
#include <ctype.h>
#include <errno.h>
+#include <inttypes.h>
#include <limits.h>
#include <stdarg.h>
#include <stdint.h>
@@ -26,61 +31,46 @@
#include <sys/stat.h>
#include <sys/types.h>
+#include <string>
+
+#include <base/stringprintf.h>
+
#if !defined(_WIN32)
#include <termios.h>
#include <unistd.h>
#endif
-#include "sysdeps.h"
-
-#define TRACE_TAG TRACE_ADB
#include "adb.h"
#include "adb_auth.h"
#include "adb_client.h"
#include "adb_io.h"
+#include "adb_utils.h"
#include "file_sync_service.h"
-static int do_cmd(transport_type ttype, const char* serial, const char *cmd, ...);
+static int install_app(TransportType t, const char* serial, int argc, const char** argv);
+static int install_multiple_app(TransportType t, const char* serial, int argc, const char** argv);
+static int uninstall_app(TransportType t, const char* serial, int argc, const char** argv);
-int find_sync_dirs(const char *srcarg,
- char **system_srcdir_out, char **data_srcdir_out, char **vendor_srcdir_out,
- char **oem_srcdir_out);
-int install_app(transport_type transport, const char* serial, int argc,
- const char** argv);
-int install_multiple_app(transport_type transport, const char* serial, int argc,
- const char** argv);
-int uninstall_app(transport_type transport, const char* serial, int argc,
- const char** argv);
-
-static const char *gProductOutPath = NULL;
+static std::string gProductOutPath;
extern int gListenAll;
-static char *product_file(const char *extra)
-{
- if (gProductOutPath == NULL) {
+static std::string product_file(const char *extra) {
+ if (gProductOutPath.empty()) {
fprintf(stderr, "adb: Product directory not specified; "
"use -p or define ANDROID_PRODUCT_OUT\n");
exit(1);
}
- int n = strlen(gProductOutPath) + strlen(extra) + 2;
- char* x = reinterpret_cast<char*>(malloc(n));
- if (x == 0) {
- fprintf(stderr, "adb: Out of memory (product_file())\n");
- exit(1);
- }
-
- snprintf(x, (size_t)n, "%s" OS_PATH_SEPARATOR_STR "%s", gProductOutPath, extra);
- return x;
+ return android::base::StringPrintf("%s%s%s",
+ gProductOutPath.c_str(), OS_PATH_SEPARATOR_STR, extra);
}
-void version(FILE * out) {
- fprintf(out, "Android Debug Bridge version %d.%d.%d\n",
- ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION);
+static void version(FILE* out) {
+ fprintf(out, "Android Debug Bridge version %d.%d.%d\nRevision %s\n",
+ ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_REVISION);
}
-void help()
-{
+static void help() {
version(stderr);
fprintf(stderr,
@@ -206,9 +196,12 @@
" adb get-state - prints: offline | bootloader | device\n"
" adb get-serialno - prints: <serial-number>\n"
" adb get-devpath - prints: <device-path>\n"
- " adb status-window - continuously print device status for a specified device\n"
" adb remount - remounts the /system, /vendor (if present) and /oem (if present) partitions on the device read-write\n"
- " adb reboot [bootloader|recovery] - reboots the device, optionally into the bootloader or recovery program\n"
+ " adb reboot [bootloader|recovery]\n"
+ " - reboots the device, optionally into the bootloader or recovery program.\n"
+ " adb reboot sideload - reboots the device into the sideload mode in recovery program (adb root required).\n"
+ " adb reboot sideload-auto-reboot\n"
+ " - reboots into the sideload mode, then reboots automatically after the sideload regardless of the result.\n"
" adb reboot-bootloader - reboots the device into the bootloader\n"
" adb root - restarts the adbd daemon with root permissions\n"
" adb unroot - restarts the adbd daemon without root permissions\n"
@@ -236,67 +229,50 @@
);
}
-int usage()
-{
+static int usage() {
help();
return 1;
}
#if defined(_WIN32)
-// Windows does not have <termio.h>.
-static void stdin_raw_init(int fd) {
-
-}
-
-static void stdin_raw_restore(int fd) {
-
-}
+// Implemented in sysdeps_win32.cpp.
+void stdin_raw_init(int fd);
+void stdin_raw_restore(int fd);
#else
-static struct termios tio_save;
+static termios g_saved_terminal_state;
-static void stdin_raw_init(int fd)
-{
- struct termios tio;
+static void stdin_raw_init(int fd) {
+ if (tcgetattr(fd, &g_saved_terminal_state)) return;
- if(tcgetattr(fd, &tio)) return;
- if(tcgetattr(fd, &tio_save)) return;
+ termios tio;
+ if (tcgetattr(fd, &tio)) return;
- tio.c_lflag = 0; /* disable CANON, ECHO*, etc */
+ cfmakeraw(&tio);
- /* no timeout but request at least one character per read */
+ // No timeout but request at least one character per read.
tio.c_cc[VTIME] = 0;
tio.c_cc[VMIN] = 1;
- tcsetattr(fd, TCSANOW, &tio);
- tcflush(fd, TCIFLUSH);
+ tcsetattr(fd, TCSAFLUSH, &tio);
}
-static void stdin_raw_restore(int fd)
-{
- tcsetattr(fd, TCSANOW, &tio_save);
- tcflush(fd, TCIFLUSH);
+static void stdin_raw_restore(int fd) {
+ tcsetattr(fd, TCSAFLUSH, &g_saved_terminal_state);
}
#endif
-static void read_and_dump(int fd)
-{
- char buf[4096];
- int len;
-
- while(fd >= 0) {
+static void read_and_dump(int fd) {
+ while (fd >= 0) {
D("read_and_dump(): pre adb_read(fd=%d)\n", fd);
- len = adb_read(fd, buf, 4096);
+ char buf[BUFSIZ];
+ int len = adb_read(fd, buf, sizeof(buf));
D("read_and_dump(): post adb_read(fd=%d): len=%d\n", fd, len);
- if(len == 0) {
+ if (len <= 0) {
break;
}
- if(len < 0) {
- if(errno == EINTR) continue;
- break;
- }
fwrite(buf, 1, len, stdout);
fflush(stdout);
}
@@ -323,6 +299,7 @@
static void copy_to_file(int inFd, int outFd) {
const size_t BUFSIZE = 32 * 1024;
char* buf = (char*) malloc(BUFSIZE);
+ if (buf == nullptr) fatal("couldn't allocate buffer for copy_to_file");
int len;
long total = 0;
@@ -332,7 +309,7 @@
stdin_raw_init(STDIN_FILENO);
}
- for (;;) {
+ while (true) {
if (inFd == STDIN_FILENO) {
len = unix_read(inFd, buf, BUFSIZE);
} else {
@@ -418,97 +395,91 @@
return 0;
}
-int interactive_shell(void)
-{
- adb_thread_t thr;
- int fdi, fd;
+static int interactive_shell() {
+ int fdi;
- fd = adb_connect("shell:");
- if(fd < 0) {
- fprintf(stderr,"error: %s\n", adb_error());
+ std::string error;
+ int fd = adb_connect("shell:", &error);
+ if (fd < 0) {
+ fprintf(stderr,"error: %s\n", error.c_str());
return 1;
}
fdi = 0; //dup(0);
int* fds = reinterpret_cast<int*>(malloc(sizeof(int) * 2));
+ if (fds == nullptr) {
+ fprintf(stderr, "couldn't allocate fds array: %s\n", strerror(errno));
+ return 1;
+ }
+
fds[0] = fd;
fds[1] = fdi;
stdin_raw_init(fdi);
- adb_thread_create(&thr, stdin_read_thread, fds);
+
+ adb_thread_create(stdin_read_thread, fds);
read_and_dump(fd);
stdin_raw_restore(fdi);
return 0;
}
-static void format_host_command(char* buffer, size_t buflen, const char* command, transport_type ttype, const char* serial)
-{
+static std::string format_host_command(const char* command, TransportType type, const char* serial) {
if (serial) {
- snprintf(buffer, buflen, "host-serial:%s:%s", serial, command);
- } else {
- const char* prefix = "host";
- if (ttype == kTransportUsb)
- prefix = "host-usb";
- else if (ttype == kTransportLocal)
- prefix = "host-local";
-
- snprintf(buffer, buflen, "%s:%s", prefix, command);
+ return android::base::StringPrintf("host-serial:%s:%s", serial, command);
}
+
+ const char* prefix = "host";
+ if (type == kTransportUsb) {
+ prefix = "host-usb";
+ } else if (type == kTransportLocal) {
+ prefix = "host-local";
+ }
+ return android::base::StringPrintf("%s:%s", prefix, command);
}
-int adb_download_buffer(const char *service, const char *fn, const void* data, int sz,
- unsigned progress)
+static int adb_download_buffer(const char *service, const char *fn, const void* data, unsigned sz,
+ bool show_progress)
{
- char buf[4096];
- unsigned total;
- int fd;
-
- sprintf(buf,"%s:%d", service, sz);
- fd = adb_connect(buf);
- if(fd < 0) {
- fprintf(stderr,"error: %s\n", adb_error());
+ std::string error;
+ int fd = adb_connect(android::base::StringPrintf("%s:%d", service, sz), &error);
+ if (fd < 0) {
+ fprintf(stderr,"error: %s\n", error.c_str());
return -1;
}
int opt = CHUNK_SIZE;
opt = adb_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const void *) &opt, sizeof(opt));
- total = sz;
+ unsigned total = sz;
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data);
- if(progress) {
+ if (show_progress) {
char *x = strrchr(service, ':');
if(x) service = x + 1;
}
- while(sz > 0) {
+ while (sz > 0) {
unsigned xfer = (sz > CHUNK_SIZE) ? CHUNK_SIZE : sz;
- if(!WriteFdExactly(fd, ptr, xfer)) {
- adb_status(fd);
- fprintf(stderr,"* failed to write data '%s' *\n", adb_error());
+ if (!WriteFdExactly(fd, ptr, xfer)) {
+ std::string error;
+ adb_status(fd, &error);
+ fprintf(stderr,"* failed to write data '%s' *\n", error.c_str());
return -1;
}
sz -= xfer;
ptr += xfer;
- if(progress) {
+ if (show_progress) {
printf("sending: '%s' %4d%% \r", fn, (int)(100LL - ((100LL * sz) / (total))));
fflush(stdout);
}
}
- if(progress) {
+ if (show_progress) {
printf("\n");
}
- if(!ReadFdExactly(fd, buf, 4)){
- fprintf(stderr,"* error reading response *\n");
- adb_close(fd);
- return -1;
- }
- if(memcmp(buf, "OKAY", 4)) {
- buf[4] = 0;
- fprintf(stderr,"* error response '%s' *\n", buf);
- adb_close(fd);
+ if (!adb_status(fd, &error)) {
+ fprintf(stderr,"* error response '%s' *\n", error.c_str());
return -1;
}
@@ -516,23 +487,6 @@
return 0;
}
-
-int adb_download(const char *service, const char *fn, unsigned progress)
-{
- void *data;
- unsigned sz;
-
- data = load_file(fn, &sz);
- if(data == 0) {
- fprintf(stderr,"* cannot read '%s' *\n", fn);
- return -1;
- }
-
- int status = adb_download_buffer(service, fn, data, sz, progress);
- free(data);
- return status;
-}
-
#define SIDELOAD_HOST_BLOCK_SIZE (CHUNK_SIZE)
/*
@@ -554,7 +508,7 @@
* - When the other side sends "DONEDONE" instead of a block number,
* we hang up.
*/
-int adb_sideload_host(const char* fn) {
+static int adb_sideload_host(const char* fn) {
unsigned sz;
size_t xfer = 0;
int status;
@@ -570,37 +524,39 @@
return -1;
}
- char buf[100];
- sprintf(buf, "sideload-host:%d:%d", sz, SIDELOAD_HOST_BLOCK_SIZE);
- int fd = adb_connect(buf);
+ std::string service =
+ android::base::StringPrintf("sideload-host:%d:%d", sz, SIDELOAD_HOST_BLOCK_SIZE);
+ std::string error;
+ int fd = adb_connect(service, &error);
if (fd < 0) {
// Try falling back to the older sideload method. Maybe this
// is an older device that doesn't support sideload-host.
printf("\n");
- status = adb_download_buffer("sideload", fn, data, sz, 1);
+ status = adb_download_buffer("sideload", fn, data, sz, true);
goto done;
}
opt = adb_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const void *) &opt, sizeof(opt));
- for (;;) {
+ while (true) {
+ char buf[9];
if (!ReadFdExactly(fd, buf, 8)) {
- fprintf(stderr, "* failed to read command: %s\n", adb_error());
+ fprintf(stderr, "* failed to read command: %s\n", strerror(errno));
status = -1;
goto done;
}
+ buf[8] = '\0';
- if (strncmp("DONEDONE", buf, 8) == 0) {
+ if (strcmp("DONEDONE", buf) == 0) {
status = 0;
break;
}
- buf[8] = '\0';
int block = strtol(buf, NULL, 10);
size_t offset = block * SIDELOAD_HOST_BLOCK_SIZE;
if (offset >= sz) {
- fprintf(stderr, "* attempt to read past end: %s\n", adb_error());
+ fprintf(stderr, "* attempt to read block %d past end\n", block);
status = -1;
goto done;
}
@@ -612,8 +568,8 @@
}
if(!WriteFdExactly(fd, start, to_write)) {
- adb_status(fd);
- fprintf(stderr,"* failed to write data '%s' *\n", adb_error());
+ adb_status(fd, &error);
+ fprintf(stderr,"* failed to write data '%s' *\n", error.c_str());
status = -1;
goto done;
}
@@ -641,94 +597,6 @@
return status;
}
-static void status_window(transport_type ttype, const char* serial)
-{
- char command[4096];
- char *state = 0;
- char *laststate = 0;
-
- /* silence stderr */
-#ifdef _WIN32
- /* XXX: TODO */
-#else
- int fd;
- fd = unix_open("/dev/null", O_WRONLY);
- dup2(fd, 2);
- adb_close(fd);
-#endif
-
- format_host_command(command, sizeof command, "get-state", ttype, serial);
-
- for(;;) {
- adb_sleep_ms(250);
-
- if(state) {
- free(state);
- state = 0;
- }
-
- state = adb_query(command);
-
- if(state) {
- if(laststate && !strcmp(state,laststate)){
- continue;
- } else {
- if(laststate) free(laststate);
- laststate = strdup(state);
- }
- }
-
- printf("%c[2J%c[2H", 27, 27);
- printf("Android Debug Bridge\n");
- printf("State: %s\n", state ? state : "offline");
- fflush(stdout);
- }
-}
-
-static int should_escape(const char c)
-{
- return (c == ' ' || c == '\'' || c == '"' || c == '\\' || c == '(' || c == ')');
-}
-
-/* Duplicate and escape given argument. */
-static char *escape_arg(const char *s)
-{
- const char *ts;
- size_t alloc_len;
- char *ret;
- char *dest;
-
- alloc_len = 0;
- for (ts = s; *ts != '\0'; ts++) {
- alloc_len++;
- if (should_escape(*ts)) {
- alloc_len++;
- }
- }
-
- if (alloc_len == 0) {
- // Preserve empty arguments
- ret = (char *) malloc(3);
- ret[0] = '\"';
- ret[1] = '\"';
- ret[2] = '\0';
- return ret;
- }
-
- ret = (char *) malloc(alloc_len + 1);
- dest = ret;
-
- for (ts = s; *ts != '\0'; ts++) {
- if (should_escape(*ts)) {
- *dest++ = '\\';
- }
- *dest++ = *ts;
- }
- *dest++ = '\0';
-
- return ret;
-}
-
/**
* Run ppp in "notty" mode against a resource listed as the first parameter
* eg:
@@ -736,15 +604,11 @@
* ppp dev:/dev/omap_csmi_tty0 <ppp options>
*
*/
-int ppp(int argc, const char **argv)
-{
+static int ppp(int argc, const char** argv) {
#if defined(_WIN32)
fprintf(stderr, "error: adb %s not implemented on Win32\n", argv[0]);
return -1;
#else
- pid_t pid;
- int fd;
-
if (argc < 2) {
fprintf(stderr, "usage: adb %s <adb service name> [ppp opts]\n",
argv[0]);
@@ -753,15 +617,15 @@
}
const char* adb_service_name = argv[1];
- fd = adb_connect(adb_service_name);
-
- if(fd < 0) {
+ std::string error;
+ int fd = adb_connect(adb_service_name, &error);
+ if (fd < 0) {
fprintf(stderr,"Error: Could not open adb service: %s. Error: %s\n",
- adb_service_name, adb_error());
+ adb_service_name, error.c_str());
return 1;
}
- pid = fork();
+ pid_t pid = fork();
if (pid < 0) {
perror("from fork()");
@@ -802,57 +666,69 @@
#endif /* !defined(_WIN32) */
}
-static int send_shellcommand(transport_type transport, const char* serial,
- char* buf)
-{
- int fd, ret;
+static bool wait_for_device(const char* service, TransportType t, const char* serial) {
+ // Was the caller vague about what they'd like us to wait for?
+ // If so, check they weren't more specific in their choice of transport type.
+ if (strcmp(service, "wait-for-device") == 0) {
+ if (t == kTransportUsb) {
+ service = "wait-for-usb";
+ } else if (t == kTransportLocal) {
+ service = "wait-for-local";
+ } else {
+ service = "wait-for-any";
+ }
+ }
- for(;;) {
- fd = adb_connect(buf);
- if(fd >= 0)
+ std::string cmd = format_host_command(service, t, serial);
+ std::string error;
+ if (adb_command(cmd, &error)) {
+ D("failure: %s *\n", error.c_str());
+ fprintf(stderr,"error: %s\n", error.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+static int send_shell_command(TransportType transport_type, const char* serial,
+ const std::string& command) {
+ int fd;
+ while (true) {
+ std::string error;
+ fd = adb_connect(command, &error);
+ if (fd >= 0) {
break;
+ }
fprintf(stderr,"- waiting for device -\n");
adb_sleep_ms(1000);
- do_cmd(transport, serial, "wait-for-device", 0);
+ wait_for_device("wait-for-device", transport_type, serial);
}
read_and_dump(fd);
- ret = adb_close(fd);
- if (ret)
+ int rc = adb_close(fd);
+ if (rc) {
perror("close");
-
- return ret;
+ }
+ return rc;
}
-static int logcat(transport_type transport, const char* serial, int argc,
- const char** argv)
-{
- char buf[4096];
+static int logcat(TransportType transport, const char* serial, int argc, const char** argv) {
+ char* log_tags = getenv("ANDROID_LOG_TAGS");
+ std::string quoted = escape_arg(log_tags == nullptr ? "" : log_tags);
- char *log_tags;
- char *quoted;
-
- log_tags = getenv("ANDROID_LOG_TAGS");
- quoted = escape_arg(log_tags == NULL ? "" : log_tags);
- snprintf(buf, sizeof(buf),
- "shell:export ANDROID_LOG_TAGS=\"%s\"; exec logcat", quoted);
- free(quoted);
+ std::string cmd = "shell:export ANDROID_LOG_TAGS=\"" + quoted + "\"; exec logcat";
if (!strcmp(argv[0], "longcat")) {
- strncat(buf, " -v long", sizeof(buf) - 1);
+ cmd += " -v long";
}
- argc -= 1;
- argv += 1;
- while(argc-- > 0) {
- quoted = escape_arg(*argv++);
- strncat(buf, " ", sizeof(buf) - 1);
- strncat(buf, quoted, sizeof(buf) - 1);
- free(quoted);
+ --argc;
+ ++argv;
+ while (argc-- > 0) {
+ cmd += " " + escape_arg(*argv++);
}
- send_shellcommand(transport, serial, buf);
- return 0;
+ return send_shell_command(transport, serial, cmd);
}
static int mkdirs(const char *path)
@@ -875,21 +751,17 @@
}
static int backup(int argc, const char** argv) {
- char buf[4096];
- char default_name[32];
- const char* filename = strcpy(default_name, "./backup.ab");
- int fd, outFd;
- int i, j;
+ const char* filename = "./backup.ab";
/* find, extract, and use any -f argument */
- for (i = 1; i < argc; i++) {
+ for (int i = 1; i < argc; i++) {
if (!strcmp("-f", argv[i])) {
if (i == argc-1) {
fprintf(stderr, "adb: -f passed with no filename\n");
return usage();
}
filename = argv[i+1];
- for (j = i+2; j <= argc; ) {
+ for (int j = i+2; j <= argc; ) {
argv[i++] = argv[j++];
}
argc -= 2;
@@ -902,22 +774,24 @@
adb_unlink(filename);
mkdirs(filename);
- outFd = adb_creat(filename, 0640);
+ int outFd = adb_creat(filename, 0640);
if (outFd < 0) {
fprintf(stderr, "adb: unable to open file %s\n", filename);
return -1;
}
- snprintf(buf, sizeof(buf), "backup");
- for (argc--, argv++; argc; argc--, argv++) {
- strncat(buf, ":", sizeof(buf) - strlen(buf) - 1);
- strncat(buf, argv[0], sizeof(buf) - strlen(buf) - 1);
+ std::string cmd = "backup:";
+ --argc;
+ ++argv;
+ while (argc-- > 0) {
+ cmd += " " + escape_arg(*argv++);
}
- D("backup. filename=%s buf=%s\n", filename, buf);
- fd = adb_connect(buf);
+ D("backup. filename=%s cmd=%s\n", filename, cmd.c_str());
+ std::string error;
+ int fd = adb_connect(cmd, &error);
if (fd < 0) {
- fprintf(stderr, "adb: unable to connect for backup\n");
+ fprintf(stderr, "adb: unable to connect for backup: %s\n", error.c_str());
adb_close(outFd);
return -1;
}
@@ -931,21 +805,19 @@
}
static int restore(int argc, const char** argv) {
- const char* filename;
- int fd, tarFd;
-
if (argc != 2) return usage();
- filename = argv[1];
- tarFd = adb_open(filename, O_RDONLY);
+ const char* filename = argv[1];
+ int tarFd = adb_open(filename, O_RDONLY);
if (tarFd < 0) {
- fprintf(stderr, "adb: unable to open file %s\n", filename);
+ fprintf(stderr, "adb: unable to open file %s: %s\n", filename, strerror(errno));
return -1;
}
- fd = adb_connect("restore:");
+ std::string error;
+ int fd = adb_connect("restore:", &error);
if (fd < 0) {
- fprintf(stderr, "adb: unable to connect for restore\n");
+ fprintf(stderr, "adb: unable to connect for restore: %s\n", error.c_str());
adb_close(tarFd);
return -1;
}
@@ -958,81 +830,9 @@
return 0;
}
-#define SENTINEL_FILE "config" OS_PATH_SEPARATOR_STR "envsetup.make"
-static int top_works(const char *top)
-{
- if (top != NULL && adb_is_absolute_host_path(top)) {
- char path_buf[PATH_MAX];
- snprintf(path_buf, sizeof(path_buf),
- "%s" OS_PATH_SEPARATOR_STR SENTINEL_FILE, top);
- return access(path_buf, F_OK) == 0;
- }
- return 0;
-}
-
-static char *find_top_from(const char *indir, char path_buf[PATH_MAX])
-{
- strcpy(path_buf, indir);
- while (1) {
- if (top_works(path_buf)) {
- return path_buf;
- }
- char *s = adb_dirstop(path_buf);
- if (s != NULL) {
- *s = '\0';
- } else {
- path_buf[0] = '\0';
- return NULL;
- }
- }
-}
-
-static char *find_top(char path_buf[PATH_MAX])
-{
- char *top = getenv("ANDROID_BUILD_TOP");
- if (top != NULL && top[0] != '\0') {
- if (!top_works(top)) {
- fprintf(stderr, "adb: bad ANDROID_BUILD_TOP value \"%s\"\n", top);
- return NULL;
- }
- } else {
- top = getenv("TOP");
- if (top != NULL && top[0] != '\0') {
- if (!top_works(top)) {
- fprintf(stderr, "adb: bad TOP value \"%s\"\n", top);
- return NULL;
- }
- } else {
- top = NULL;
- }
- }
-
- if (top != NULL) {
- /* The environment pointed to a top directory that works.
- */
- strcpy(path_buf, top);
- return path_buf;
- }
-
- /* The environment didn't help. Walk up the tree from the CWD
- * to see if we can find the top.
- */
- char dir[PATH_MAX];
- top = find_top_from(getcwd(dir, sizeof(dir)), path_buf);
- if (top == NULL) {
- /* If the CWD isn't under a good-looking top, see if the
- * executable is.
- */
- get_my_path(dir, PATH_MAX);
- top = find_top_from(dir, path_buf);
- }
- return top;
-}
-
/* <hint> may be:
* - A simple product name
* e.g., "sooner"
-TODO: debug? sooner-debug, sooner:debug?
* - A relative path from the CWD to the ANDROID_PRODUCT_OUT dir
* e.g., "out/target/product/sooner"
* - An absolute path to the PRODUCT_OUT dir
@@ -1041,62 +841,52 @@
* Given <hint>, try to construct an absolute path to the
* ANDROID_PRODUCT_OUT dir.
*/
-static const char *find_product_out_path(const char *hint)
-{
- static char path_buf[PATH_MAX];
-
+static std::string find_product_out_path(const char* hint) {
if (hint == NULL || hint[0] == '\0') {
- return NULL;
+ return "";
}
- /* If it's already absolute, don't bother doing any work.
- */
+ // If it's already absolute, don't bother doing any work.
if (adb_is_absolute_host_path(hint)) {
- strcpy(path_buf, hint);
- return path_buf;
+ return hint;
}
- /* If there are any slashes in it, assume it's a relative path;
- * make it absolute.
- */
- if (adb_dirstart(hint) != NULL) {
- if (getcwd(path_buf, sizeof(path_buf)) == NULL) {
- fprintf(stderr, "adb: Couldn't get CWD: %s\n", strerror(errno));
- return NULL;
+ // If there are any slashes in it, assume it's a relative path;
+ // make it absolute.
+ if (adb_dirstart(hint) != nullptr) {
+ std::string cwd;
+ if (!getcwd(&cwd)) {
+ fprintf(stderr, "adb: getcwd failed: %s\n", strerror(errno));
+ return "";
}
- if (strlen(path_buf) + 1 + strlen(hint) >= sizeof(path_buf)) {
- fprintf(stderr, "adb: Couldn't assemble path\n");
- return NULL;
- }
- strcat(path_buf, OS_PATH_SEPARATOR_STR);
- strcat(path_buf, hint);
- return path_buf;
+ return android::base::StringPrintf("%s%s%s", cwd.c_str(), OS_PATH_SEPARATOR_STR, hint);
}
- /* It's a string without any slashes. Try to do something with it.
- *
- * Try to find the root of the build tree, and build a PRODUCT_OUT
- * path from there.
- */
- char top_buf[PATH_MAX];
- const char *top = find_top(top_buf);
- if (top == NULL) {
- fprintf(stderr, "adb: Couldn't find top of build tree\n");
- return NULL;
+ // It's a string without any slashes. Try to do something with it.
+ //
+ // Try to find the root of the build tree, and build a PRODUCT_OUT
+ // path from there.
+ char* top = getenv("ANDROID_BUILD_TOP");
+ if (top == nullptr) {
+ fprintf(stderr, "adb: ANDROID_BUILD_TOP not set!\n");
+ return "";
}
-//TODO: if we have a way to indicate debug, look in out/debug/target/...
- snprintf(path_buf, sizeof(path_buf),
- "%s" OS_PATH_SEPARATOR_STR
- "out" OS_PATH_SEPARATOR_STR
- "target" OS_PATH_SEPARATOR_STR
- "product" OS_PATH_SEPARATOR_STR
- "%s", top_buf, hint);
- if (access(path_buf, F_OK) < 0) {
- fprintf(stderr, "adb: Couldn't find a product dir "
- "based on \"-p %s\"; \"%s\" doesn't exist\n", hint, path_buf);
- return NULL;
+
+ std::string path = top;
+ path += OS_PATH_SEPARATOR_STR;
+ path += "out";
+ path += OS_PATH_SEPARATOR_STR;
+ path += "target";
+ path += OS_PATH_SEPARATOR_STR;
+ path += "product";
+ path += OS_PATH_SEPARATOR_STR;
+ path += hint;
+ if (!directory_exists(path)) {
+ fprintf(stderr, "adb: Couldn't find a product dir based on -p %s; "
+ "\"%s\" doesn't exist\n", hint, path.c_str());
+ return "";
}
- return path_buf;
+ return path;
}
static void parse_push_pull_args(const char **arg, int narg, char const **path1,
@@ -1128,34 +918,51 @@
}
}
-int adb_commandline(int argc, const char **argv)
-{
- char buf[4096];
+static int adb_connect_command(const std::string& command) {
+ std::string error;
+ int fd = adb_connect(command, &error);
+ if (fd < 0) {
+ fprintf(stderr, "error: %s\n", error.c_str());
+ return 1;
+ }
+ read_and_dump(fd);
+ adb_close(fd);
+ return 0;
+}
+
+static int adb_query_command(const std::string& command) {
+ std::string result;
+ std::string error;
+ if (!adb_query(command, &result, &error)) {
+ fprintf(stderr, "error: %s\n", error.c_str());
+ return 1;
+ }
+ printf("%s\n", result.c_str());
+ return 0;
+}
+
+int adb_commandline(int argc, const char **argv) {
int no_daemon = 0;
int is_daemon = 0;
int is_server = 0;
- int persist = 0;
int r;
- transport_type ttype = kTransportAny;
- const char* serial = NULL;
- const char* server_port_str = NULL;
+ TransportType transport_type = kTransportAny;
- /* If defined, this should be an absolute path to
- * the directory containing all of the various system images
- * for a particular product. If not defined, and the adb
- * command requires this information, then the user must
- * specify the path using "-p".
- */
- gProductOutPath = getenv("ANDROID_PRODUCT_OUT");
- if (gProductOutPath == NULL || gProductOutPath[0] == '\0') {
- gProductOutPath = NULL;
+ // If defined, this should be an absolute path to
+ // the directory containing all of the various system images
+ // for a particular product. If not defined, and the adb
+ // command requires this information, then the user must
+ // specify the path using "-p".
+ char* ANDROID_PRODUCT_OUT = getenv("ANDROID_PRODUCT_OUT");
+ if (ANDROID_PRODUCT_OUT != nullptr) {
+ gProductOutPath = ANDROID_PRODUCT_OUT;
}
// TODO: also try TARGET_PRODUCT/TARGET_DEVICE as a hint
- serial = getenv("ANDROID_SERIAL");
+ const char* serial = getenv("ANDROID_SERIAL");
/* Validate and assign the server port */
- server_port_str = getenv("ANDROID_ADB_SERVER_PORT");
+ const char* server_port_str = getenv("ANDROID_ADB_SERVER_PORT");
int server_port = DEFAULT_ADB_PORT;
if (server_port_str && strlen(server_port_str) > 0) {
server_port = (int) strtol(server_port_str, NULL, 0);
@@ -1176,8 +983,6 @@
} else if (!strcmp(argv[0], "fork-server")) {
/* this is a special flag used only when the ADB client launches the ADB Server */
is_daemon = 1;
- } else if (!strcmp(argv[0],"persist")) {
- persist = 1;
} else if (!strncmp(argv[0], "-p", 2)) {
const char *product = NULL;
if (argv[0][2] == '\0') {
@@ -1189,9 +994,8 @@
product = argv[0] + 2;
}
gProductOutPath = find_product_out_path(product);
- if (gProductOutPath == NULL) {
- fprintf(stderr, "adb: could not resolve \"-p %s\"\n",
- product);
+ if (gProductOutPath.empty()) {
+ fprintf(stderr, "adb: could not resolve \"-p %s\"\n", product);
return usage();
}
} else if (argv[0][0]=='-' && argv[0][1]=='s') {
@@ -1204,9 +1008,9 @@
argv++;
}
} else if (!strcmp(argv[0],"-d")) {
- ttype = kTransportUsb;
+ transport_type = kTransportUsb;
} else if (!strcmp(argv[0],"-e")) {
- ttype = kTransportLocal;
+ transport_type = kTransportLocal;
} else if (!strcmp(argv[0],"-a")) {
gListenAll = 1;
} else if (!strncmp(argv[0], "-H", 2)) {
@@ -1251,7 +1055,7 @@
argv++;
}
- adb_set_transport(ttype, serial);
+ adb_set_transport(transport_type, serial);
adb_set_tcp_specifics(server_port);
if (is_server) {
@@ -1273,27 +1077,13 @@
/* handle wait-for-* prefix */
if (!strncmp(argv[0], "wait-for-", strlen("wait-for-"))) {
const char* service = argv[0];
- if (!strncmp(service, "wait-for-device", strlen("wait-for-device"))) {
- if (ttype == kTransportUsb) {
- service = "wait-for-usb";
- } else if (ttype == kTransportLocal) {
- service = "wait-for-local";
- } else {
- service = "wait-for-any";
- }
- }
- format_host_command(buf, sizeof buf, service, ttype, serial);
-
- if (adb_command(buf)) {
- D("failure: %s *\n",adb_error());
- fprintf(stderr,"error: %s\n", adb_error());
+ if (!wait_for_device(service, transport_type, serial)) {
return 1;
}
- /* Allow a command to be run after wait-for-device,
- * e.g. 'adb wait-for-device shell'.
- */
+ // Allow a command to be run after wait-for-device,
+ // e.g. 'adb wait-for-device shell'.
if (argc == 1) {
return 0;
}
@@ -1305,67 +1095,43 @@
/* adb_connect() commands */
if (!strcmp(argv[0], "devices")) {
- char *tmp;
const char *listopt;
- if (argc < 2)
+ if (argc < 2) {
listopt = "";
- else if (argc == 2 && !strcmp(argv[1], "-l"))
+ } else if (argc == 2 && !strcmp(argv[1], "-l")) {
listopt = argv[1];
- else {
+ } else {
fprintf(stderr, "Usage: adb devices [-l]\n");
return 1;
}
- snprintf(buf, sizeof buf, "host:%s%s", argv[0], listopt);
- tmp = adb_query(buf);
- if (tmp) {
- printf("List of devices attached \n");
- printf("%s\n", tmp);
- return 0;
- } else {
- return 1;
- }
+
+ std::string query = android::base::StringPrintf("host:%s%s", argv[0], listopt);
+ printf("List of devices attached\n");
+ return adb_query_command(query);
}
else if (!strcmp(argv[0], "connect")) {
- char *tmp;
if (argc != 2) {
fprintf(stderr, "Usage: adb connect <host>[:<port>]\n");
return 1;
}
- snprintf(buf, sizeof buf, "host:connect:%s", argv[1]);
- tmp = adb_query(buf);
- if (tmp) {
- printf("%s\n", tmp);
- return 0;
- } else {
- return 1;
- }
+
+ std::string query = android::base::StringPrintf("host:connect:%s", argv[1]);
+ return adb_query_command(query);
}
else if (!strcmp(argv[0], "disconnect")) {
- char *tmp;
if (argc > 2) {
fprintf(stderr, "Usage: adb disconnect [<host>[:<port>]]\n");
return 1;
}
- if (argc == 2) {
- snprintf(buf, sizeof buf, "host:disconnect:%s", argv[1]);
- } else {
- snprintf(buf, sizeof buf, "host:disconnect:");
- }
- tmp = adb_query(buf);
- if (tmp) {
- printf("%s\n", tmp);
- return 0;
- } else {
- return 1;
- }
+
+ std::string query = android::base::StringPrintf("host:disconnect:%s",
+ (argc == 2) ? argv[1] : "");
+ return adb_query_command(query);
}
else if (!strcmp(argv[0], "emu")) {
- return adb_send_emulator_command(argc, argv);
+ return adb_send_emulator_command(argc, argv, serial);
}
else if (!strcmp(argv[0], "shell") || !strcmp(argv[0], "hell")) {
- int r;
- int fd;
-
char h = (argv[0][0] == 'h');
if (h) {
@@ -1383,19 +1149,20 @@
return r;
}
- snprintf(buf, sizeof(buf), "shell:%s", argv[1]);
- argc -= 2;
- argv += 2;
+ std::string cmd = "shell:";
+ --argc;
+ ++argv;
while (argc-- > 0) {
- char *quoted = escape_arg(*argv++);
- strncat(buf, " ", sizeof(buf) - 1);
- strncat(buf, quoted, sizeof(buf) - 1);
- free(quoted);
+ // We don't escape here, just like ssh(1). http://b/20564385.
+ cmd += *argv++;
+ if (*argv) cmd += " ";
}
- for(;;) {
- D("interactive shell loop. buff=%s\n", buf);
- fd = adb_connect(buf);
+ while (true) {
+ D("interactive shell loop. cmd=%s\n", cmd.c_str());
+ std::string error;
+ int fd = adb_connect(cmd, &error);
+ int r;
if (fd >= 0) {
D("about to read_and_dump(fd=%d)\n", fd);
read_and_dump(fd);
@@ -1403,41 +1170,33 @@
adb_close(fd);
r = 0;
} else {
- fprintf(stderr,"error: %s\n", adb_error());
+ fprintf(stderr,"error: %s\n", error.c_str());
r = -1;
}
- if (persist) {
- fprintf(stderr,"\n- waiting for device -\n");
- adb_sleep_ms(1000);
- do_cmd(ttype, serial, "wait-for-device", 0);
- } else {
- if (h) {
- printf("\x1b[0m");
- fflush(stdout);
- }
- D("interactive shell loop. return r=%d\n", r);
- return r;
+ if (h) {
+ printf("\x1b[0m");
+ fflush(stdout);
}
+ D("interactive shell loop. return r=%d\n", r);
+ return r;
}
}
else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
int exec_in = !strcmp(argv[0], "exec-in");
- int fd;
- snprintf(buf, sizeof buf, "exec:%s", argv[1]);
+ std::string cmd = "exec:";
+ cmd += argv[1];
argc -= 2;
argv += 2;
while (argc-- > 0) {
- char *quoted = escape_arg(*argv++);
- strncat(buf, " ", sizeof(buf) - 1);
- strncat(buf, quoted, sizeof(buf) - 1);
- free(quoted);
+ cmd += " " + escape_arg(*argv++);
}
- fd = adb_connect(buf);
+ std::string error;
+ int fd = adb_connect(cmd, &error);
if (fd < 0) {
- fprintf(stderr, "error: %s\n", adb_error());
+ fprintf(stderr, "error: %s\n", error.c_str());
return -1;
}
@@ -1451,8 +1210,8 @@
return 0;
}
else if (!strcmp(argv[0], "kill-server")) {
- int fd;
- fd = _adb_connect("host:kill");
+ std::string error;
+ int fd = _adb_connect("host:kill", &error);
if (fd == -1) {
fprintf(stderr,"* server not running *\n");
return 1;
@@ -1476,29 +1235,23 @@
!strcmp(argv[0], "unroot") ||
!strcmp(argv[0], "disable-verity") ||
!strcmp(argv[0], "enable-verity")) {
- char command[100];
- if (!strcmp(argv[0], "reboot-bootloader"))
- snprintf(command, sizeof(command), "reboot:bootloader");
- else if (argc > 1)
- snprintf(command, sizeof(command), "%s:%s", argv[0], argv[1]);
- else
- snprintf(command, sizeof(command), "%s:", argv[0]);
- int fd = adb_connect(command);
- if (fd >= 0) {
- read_and_dump(fd);
- adb_close(fd);
- return 0;
+ std::string command;
+ if (!strcmp(argv[0], "reboot-bootloader")) {
+ command = "reboot:bootloader";
+ } else if (argc > 1) {
+ command = android::base::StringPrintf("%s:%s", argv[0], argv[1]);
+ } else {
+ command = android::base::StringPrintf("%s:", argv[0]);
}
- fprintf(stderr,"error: %s\n", adb_error());
- return 1;
+ return adb_connect_command(command);
}
else if (!strcmp(argv[0], "bugreport")) {
if (argc != 1) return usage();
- do_cmd(ttype, serial, "shell", "bugreport", 0);
- return 0;
+ return send_shell_command(transport_type, serial, "shell:bugreport");
}
/* adb_command() wrapper commands */
else if (!strcmp(argv[0], "forward") || !strcmp(argv[0], "reverse")) {
+ std::string cmd;
char host_prefix[64];
char reverse = (char) !strcmp(argv[0], "reverse");
char remove = 0;
@@ -1535,9 +1288,9 @@
if (serial) {
snprintf(host_prefix, sizeof host_prefix, "host-serial:%s",
serial);
- } else if (ttype == kTransportUsb) {
+ } else if (transport_type == kTransportUsb) {
snprintf(host_prefix, sizeof host_prefix, "host-usb");
- } else if (ttype == kTransportLocal) {
+ } else if (transport_type == kTransportLocal) {
snprintf(host_prefix, sizeof host_prefix, "host-local");
} else {
snprintf(host_prefix, sizeof host_prefix, "host");
@@ -1546,45 +1299,37 @@
// Implement forward --list
if (list) {
- if (argc != 1)
+ if (argc != 1) {
return usage();
- snprintf(buf, sizeof buf, "%s:list-forward", host_prefix);
- char* forwards = adb_query(buf);
- if (forwards == NULL) {
- fprintf(stderr, "error: %s\n", adb_error());
- return 1;
}
- printf("%s", forwards);
- free(forwards);
- return 0;
+
+ std::string query = android::base::StringPrintf("%s:list-forward", host_prefix);
+ return adb_query_command(query);
}
// Implement forward --remove-all
else if (remove_all) {
- if (argc != 1)
- return usage();
- snprintf(buf, sizeof buf, "%s:killforward-all", host_prefix);
+ if (argc != 1) return usage();
+ cmd = android::base::StringPrintf("%s:killforward-all", host_prefix);
}
// Implement forward --remove <local>
else if (remove) {
- if (argc != 2)
- return usage();
- snprintf(buf, sizeof buf, "%s:killforward:%s", host_prefix, argv[1]);
+ if (argc != 2) return usage();
+ cmd = android::base::StringPrintf("%s:killforward:%s", host_prefix, argv[1]);
}
// Or implement one of:
// forward <local> <remote>
// forward --no-rebind <local> <remote>
- else
- {
- if (argc != 3)
- return usage();
- const char* command = no_rebind ? "forward:norebind" : "forward";
- snprintf(buf, sizeof buf, "%s:%s:%s;%s", host_prefix, command, argv[1], argv[2]);
+ else {
+ if (argc != 3) return usage();
+ const char* command = no_rebind ? "forward:norebind" : "forward";
+ cmd = android::base::StringPrintf("%s:%s:%s;%s", host_prefix, command, argv[1], argv[2]);
}
- if (adb_command(buf)) {
- fprintf(stderr,"error: %s\n", adb_error());
+ std::string error;
+ if (adb_command(cmd, &error)) {
+ fprintf(stderr, "error: %s\n", error.c_str());
return 1;
}
return 0;
@@ -1622,87 +1367,78 @@
}
else if (!strcmp(argv[0], "install")) {
if (argc < 2) return usage();
- return install_app(ttype, serial, argc, argv);
+ return install_app(transport_type, serial, argc, argv);
}
else if (!strcmp(argv[0], "install-multiple")) {
if (argc < 2) return usage();
- return install_multiple_app(ttype, serial, argc, argv);
+ return install_multiple_app(transport_type, serial, argc, argv);
}
else if (!strcmp(argv[0], "uninstall")) {
if (argc < 2) return usage();
- return uninstall_app(ttype, serial, argc, argv);
+ return uninstall_app(transport_type, serial, argc, argv);
}
else if (!strcmp(argv[0], "sync")) {
- const char* srcarg;
- char *system_srcpath, *data_srcpath, *vendor_srcpath, *oem_srcpath;
-
- int listonly = 0;
-
- int ret;
+ std::string src;
+ bool list_only = false;
if (argc < 2) {
- /* No local path was specified. */
- srcarg = NULL;
+ // No local path was specified.
+ src = "";
} else if (argc >= 2 && strcmp(argv[1], "-l") == 0) {
- listonly = 1;
+ list_only = true;
if (argc == 3) {
- srcarg = argv[2];
+ src = argv[2];
} else {
- srcarg = NULL;
+ src = "";
}
} else if (argc == 2) {
- /* A local path or "android"/"data" arg was specified. */
- srcarg = argv[1];
+ // A local path or "android"/"data" arg was specified.
+ src = argv[1];
} else {
return usage();
}
- ret = find_sync_dirs(srcarg, &system_srcpath, &data_srcpath, &vendor_srcpath,
- &oem_srcpath);
- if (ret != 0) return usage();
- if (system_srcpath != NULL)
- ret = do_sync_sync(system_srcpath, "/system", listonly);
- if (ret == 0 && vendor_srcpath != NULL)
- ret = do_sync_sync(vendor_srcpath, "/vendor", listonly);
- if(ret == 0 && oem_srcpath != NULL)
- ret = do_sync_sync(oem_srcpath, "/oem", listonly);
- if (ret == 0 && data_srcpath != NULL)
- ret = do_sync_sync(data_srcpath, "/data", listonly);
+ if (src != "" &&
+ src != "system" && src != "data" && src != "vendor" && src != "oem") {
+ return usage();
+ }
- free(system_srcpath);
- free(vendor_srcpath);
- free(oem_srcpath);
- free(data_srcpath);
- return ret;
+ std::string system_src_path = product_file("system");
+ std::string data_src_path = product_file("data");
+ std::string vendor_src_path = product_file("vendor");
+ std::string oem_src_path = product_file("oem");
+
+ int rc = 0;
+ if (rc == 0 && (src.empty() || src == "system")) {
+ rc = do_sync_sync(system_src_path, "/system", list_only);
+ }
+ if (rc == 0 && (src.empty() || src == "vendor") && directory_exists(vendor_src_path)) {
+ rc = do_sync_sync(vendor_src_path, "/vendor", list_only);
+ }
+ if (rc == 0 && (src.empty() || src == "oem") && directory_exists(oem_src_path)) {
+ rc = do_sync_sync(oem_src_path, "/oem", list_only);
+ }
+ if (rc == 0 && (src.empty() || src == "data")) {
+ rc = do_sync_sync(data_src_path, "/data", list_only);
+ }
+ return rc;
}
/* passthrough commands */
else if (!strcmp(argv[0],"get-state") ||
!strcmp(argv[0],"get-serialno") ||
!strcmp(argv[0],"get-devpath"))
{
- char *tmp;
-
- format_host_command(buf, sizeof buf, argv[0], ttype, serial);
- tmp = adb_query(buf);
- if (tmp) {
- printf("%s\n", tmp);
- return 0;
- } else {
- return 1;
- }
+ return adb_query_command(format_host_command(argv[0], transport_type, serial));
}
/* other commands */
- else if (!strcmp(argv[0],"status-window")) {
- status_window(ttype, serial);
- return 0;
- }
else if (!strcmp(argv[0],"logcat") || !strcmp(argv[0],"lolcat") || !strcmp(argv[0],"longcat")) {
- return logcat(ttype, serial, argc, argv);
+ return logcat(transport_type, serial, argc, argv);
}
else if (!strcmp(argv[0],"ppp")) {
return ppp(argc, argv);
}
else if (!strcmp(argv[0], "start-server")) {
- return adb_connect("host:start-server");
+ std::string error;
+ return adb_connect("host:start-server", &error);
}
else if (!strcmp(argv[0], "backup")) {
return backup(argc, argv);
@@ -1715,15 +1451,7 @@
return adb_auth_keygen(argv[1]);
}
else if (!strcmp(argv[0], "jdwp")) {
- int fd = adb_connect("jdwp");
- if (fd >= 0) {
- read_and_dump(fd);
- adb_close(fd);
- return 0;
- } else {
- fprintf(stderr, "error: %s\n", adb_error());
- return -1;
- }
+ return adb_connect_command("jdwp");
}
/* "adb /?" is a common idiom under Windows */
else if (!strcmp(argv[0], "help") || !strcmp(argv[0], "/?")) {
@@ -1739,121 +1467,17 @@
return 1;
}
-#define MAX_ARGV_LENGTH 16
-static int do_cmd(transport_type ttype, const char* serial, const char *cmd, ...)
-{
- const char *argv[MAX_ARGV_LENGTH];
- int argc;
- va_list ap;
+static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
+ std::string cmd = "shell:pm";
- va_start(ap, cmd);
- argc = 0;
-
- if (serial) {
- argv[argc++] = "-s";
- argv[argc++] = serial;
- } else if (ttype == kTransportUsb) {
- argv[argc++] = "-d";
- } else if (ttype == kTransportLocal) {
- argv[argc++] = "-e";
+ while (argc-- > 0) {
+ cmd += " " + escape_arg(*argv++);
}
- argv[argc++] = cmd;
- while(argc < MAX_ARGV_LENGTH &&
- (argv[argc] = va_arg(ap, char*)) != 0) argc++;
- assert(argc < MAX_ARGV_LENGTH);
- va_end(ap);
-
-#if 0
- int n;
- fprintf(stderr,"argc = %d\n",argc);
- for(n = 0; n < argc; n++) {
- fprintf(stderr,"argv[%d] = \"%s\"\n", n, argv[n]);
- }
-#endif
-
- return adb_commandline(argc, argv);
+ return send_shell_command(transport, serial, cmd);
}
-int find_sync_dirs(const char *srcarg,
- char **system_srcdir_out, char **data_srcdir_out, char **vendor_srcdir_out,
- char **oem_srcdir_out)
-{
- char *system_srcdir = NULL, *data_srcdir = NULL, *vendor_srcdir = NULL, *oem_srcdir = NULL;
- struct stat st;
-
- if(srcarg == NULL) {
- system_srcdir = product_file("system");
- data_srcdir = product_file("data");
- vendor_srcdir = product_file("vendor");
- oem_srcdir = product_file("oem");
- // Check if vendor partition exists.
- if (lstat(vendor_srcdir, &st) || !S_ISDIR(st.st_mode))
- vendor_srcdir = NULL;
- // Check if oem partition exists.
- if (lstat(oem_srcdir, &st) || !S_ISDIR(st.st_mode))
- oem_srcdir = NULL;
- } else {
- // srcarg may be "data", "system", "vendor", "oem" or NULL.
- // If srcarg is NULL, then all partitions are synced.
- if(strcmp(srcarg, "system") == 0) {
- system_srcdir = product_file("system");
- } else if(strcmp(srcarg, "data") == 0) {
- data_srcdir = product_file("data");
- } else if(strcmp(srcarg, "vendor") == 0) {
- vendor_srcdir = product_file("vendor");
- } else if(strcmp(srcarg, "oem") == 0) {
- oem_srcdir = product_file("oem");
- } else {
- // It's not "system", "data", "vendor", or "oem".
- return 1;
- }
- }
-
- if(system_srcdir_out != NULL)
- *system_srcdir_out = system_srcdir;
- else
- free(system_srcdir);
-
- if(vendor_srcdir_out != NULL)
- *vendor_srcdir_out = vendor_srcdir;
- else
- free(vendor_srcdir);
-
- if(oem_srcdir_out != NULL)
- *oem_srcdir_out = oem_srcdir;
- else
- free(oem_srcdir);
-
- if(data_srcdir_out != NULL)
- *data_srcdir_out = data_srcdir;
- else
- free(data_srcdir);
-
- return 0;
-}
-
-static int pm_command(transport_type transport, const char* serial,
- int argc, const char** argv)
-{
- char buf[4096];
-
- snprintf(buf, sizeof(buf), "shell:pm");
-
- while(argc-- > 0) {
- char *quoted = escape_arg(*argv++);
- strncat(buf, " ", sizeof(buf) - 1);
- strncat(buf, quoted, sizeof(buf) - 1);
- free(quoted);
- }
-
- send_shellcommand(transport, serial, buf);
- return 0;
-}
-
-int uninstall_app(transport_type transport, const char* serial, int argc,
- const char** argv)
-{
+static int uninstall_app(TransportType transport, const char* serial, int argc, const char** argv) {
/* if the user choose the -k option, we refuse to do it until devices are
out with the option to uninstall the remaining data somehow (adb/ui) */
if (argc == 3 && strcmp(argv[1], "-k") == 0)
@@ -1870,18 +1494,9 @@
return pm_command(transport, serial, argc, argv);
}
-static int delete_file(transport_type transport, const char* serial, char* filename)
-{
- char buf[4096];
- char* quoted;
-
- snprintf(buf, sizeof(buf), "shell:rm -f ");
- quoted = escape_arg(filename);
- strncat(buf, quoted, sizeof(buf)-1);
- free(quoted);
-
- send_shellcommand(transport, serial, buf);
- return 0;
+static int delete_file(TransportType transport, const char* serial, char* filename) {
+ std::string cmd = "shell:rm -f " + escape_arg(filename);
+ return send_shell_command(transport, serial, cmd);
}
static const char* get_basename(const char* filename)
@@ -1895,9 +1510,7 @@
}
}
-int install_app(transport_type transport, const char* serial, int argc,
- const char** argv)
-{
+static int install_app(TransportType transport, const char* serial, int argc, const char** argv) {
static const char *const DATA_DEST = "/data/local/tmp/%s";
static const char *const SD_DEST = "/sdcard/tmp/%s";
const char* where = DATA_DEST;
@@ -1942,20 +1555,19 @@
argv[last_apk] = apk_dest; /* destination name, not source location */
}
- pm_command(transport, serial, argc, argv);
+ err = pm_command(transport, serial, argc, argv);
cleanup_apk:
delete_file(transport, serial, apk_dest);
return err;
}
-int install_multiple_app(transport_type transport, const char* serial, int argc,
- const char** argv)
+static int install_multiple_app(TransportType transport, const char* serial, int argc,
+ const char** argv)
{
- char buf[1024];
int i;
struct stat sb;
- unsigned long long total_size = 0;
+ uint64_t total_size = 0;
// Find all APK arguments starting at end.
// All other arguments passed through verbatim.
@@ -1981,20 +1593,23 @@
return 1;
}
- snprintf(buf, sizeof(buf), "exec:pm install-create -S %lld", total_size);
+#if defined(_WIN32) // Remove when we're using clang for Win32.
+ std::string cmd = android::base::StringPrintf("exec:pm install-create -S %u", (unsigned) total_size);
+#else
+ std::string cmd = android::base::StringPrintf("exec:pm install-create -S %" PRIu64, total_size);
+#endif
for (i = 1; i < first_apk; i++) {
- char *quoted = escape_arg(argv[i]);
- strncat(buf, " ", sizeof(buf) - 1);
- strncat(buf, quoted, sizeof(buf) - 1);
- free(quoted);
+ cmd += " " + escape_arg(argv[i]);
}
// Create install session
- int fd = adb_connect(buf);
+ std::string error;
+ int fd = adb_connect(cmd, &error);
if (fd < 0) {
- fprintf(stderr, "Connect error for create: %s\n", adb_error());
+ fprintf(stderr, "Connect error for create: %s\n", error.c_str());
return -1;
}
+ char buf[BUFSIZ];
read_status_line(fd, buf, sizeof(buf));
adb_close(fd);
@@ -2023,19 +1638,27 @@
goto finalize_session;
}
- snprintf(buf, sizeof(buf), "exec:pm install-write -S %lld %d %d_%s -",
- (long long int) sb.st_size, session_id, i, get_basename(file));
+#if defined(_WIN32) // Remove when we're using clang for Win32.
+ std::string cmd = android::base::StringPrintf(
+ "exec:pm install-write -S %u %d %d_%s -",
+ (unsigned) sb.st_size, session_id, i, get_basename(file));
+#else
+ std::string cmd = android::base::StringPrintf(
+ "exec:pm install-write -S %" PRIu64 " %d %d_%s -",
+ static_cast<uint64_t>(sb.st_size), session_id, i, get_basename(file));
+#endif
int localFd = adb_open(file, O_RDONLY);
if (localFd < 0) {
- fprintf(stderr, "Failed to open %s: %s\n", file, adb_error());
+ fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
success = 0;
goto finalize_session;
}
- int remoteFd = adb_connect(buf);
+ std::string error;
+ int remoteFd = adb_connect(cmd, &error);
if (remoteFd < 0) {
- fprintf(stderr, "Connect error for write: %s\n", adb_error());
+ fprintf(stderr, "Connect error for write: %s\n", error.c_str());
adb_close(localFd);
success = 0;
goto finalize_session;
@@ -2057,15 +1680,12 @@
finalize_session:
// Commit session if we streamed everything okay; otherwise abandon
- if (success) {
- snprintf(buf, sizeof(buf), "exec:pm install-commit %d", session_id);
- } else {
- snprintf(buf, sizeof(buf), "exec:pm install-abandon %d", session_id);
- }
-
- fd = adb_connect(buf);
+ std::string service =
+ android::base::StringPrintf("exec:pm install-%s %d",
+ success ? "commit" : "abandon", session_id);
+ fd = adb_connect(service, &error);
if (fd < 0) {
- fprintf(stderr, "Connect error for finalize: %s\n", adb_error());
+ fprintf(stderr, "Connect error for finalize: %s\n", error.c_str());
return -1;
}
read_status_line(fd, buf, sizeof(buf));
diff --git a/adb/console.cpp b/adb/console.cpp
index 452ee41..0707960 100644
--- a/adb/console.cpp
+++ b/adb/console.cpp
@@ -1,44 +1,115 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#include "sysdeps.h"
-#include "adb.h"
-#include "adb_client.h"
+
#include <stdio.h>
-static int connect_to_console(void)
-{
- int fd, port;
+#include "base/file.h"
+#include "base/logging.h"
+#include "base/strings.h"
- port = adb_get_emulator_console_port();
- if (port < 0) {
- if (port == -2)
- fprintf(stderr, "error: more than one emulator detected. use -s option\n");
- else
- fprintf(stderr, "error: no emulator detected\n");
+#include "adb.h"
+#include "adb_client.h"
+
+// Return the console port of the currently connected emulator (if any) or -1 if
+// there is no emulator, and -2 if there is more than one.
+static int adb_get_emulator_console_port(const char* serial) {
+ if (serial) {
+ // The user specified a serial number; is it an emulator?
+ int port;
+ return (sscanf(serial, "emulator-%d", &port) == 1) ? port : -1;
+ }
+
+ // No specific device was given, so get the list of connected devices and
+ // search for emulators. If there's one, we'll take it. If there are more
+ // than one, that's an error.
+ std::string devices;
+ std::string error;
+ if (!adb_query("host:devices", &devices, &error)) {
+ fprintf(stderr, "error: no emulator connected: %s\n", error.c_str());
return -1;
}
- fd = socket_loopback_client( port, SOCK_STREAM );
- if (fd < 0) {
+
+ int port;
+ size_t emulator_count = 0;
+ for (const auto& device : android::base::Split(devices, "\n")) {
+ if (sscanf(device.c_str(), "emulator-%d", &port) == 1) {
+ if (++emulator_count > 1) {
+ fprintf(
+ stderr, "error: more than one emulator detected; use -s\n");
+ return -1;
+ }
+ }
+ }
+
+ if (emulator_count == 0) {
+ fprintf(stderr, "error: no emulator detected\n");
+ return -1;
+ }
+
+ return port;
+}
+
+static int connect_to_console(const char* serial) {
+ int port = adb_get_emulator_console_port(serial);
+ if (port == -1) {
+ return -1;
+ }
+
+ int fd = socket_loopback_client(port, SOCK_STREAM);
+ if (fd == -1) {
fprintf(stderr, "error: could not connect to TCP port %d\n", port);
return -1;
}
- return fd;
+ return fd;
}
-
-int adb_send_emulator_command(int argc, const char** argv)
-{
- int fd, nn;
-
- fd = connect_to_console();
- if (fd < 0)
+int adb_send_emulator_command(int argc, const char** argv, const char* serial) {
+ int fd = connect_to_console(serial);
+ if (fd == -1) {
return 1;
-
-#define QUIT "quit\n"
-
- for (nn = 1; nn < argc; nn++) {
- adb_write( fd, argv[nn], strlen(argv[nn]) );
- adb_write( fd, (nn == argc-1) ? "\n" : " ", 1 );
}
- adb_write( fd, QUIT, sizeof(QUIT)-1 );
+
+ for (int i = 1; i < argc; i++) {
+ adb_write(fd, argv[i], strlen(argv[i]));
+ adb_write(fd, i == argc - 1 ? "\n" : " ", 1);
+ }
+
+ const char disconnect_command[] = "quit\n";
+ if (adb_write(fd, disconnect_command, sizeof(disconnect_command) - 1) == -1) {
+ LOG(FATAL) << "Could not finalize emulator command";
+ }
+
+ // Drain output that the emulator console has sent us to prevent a problem
+ // on Windows where if adb closes the socket without reading all the data,
+ // the emulator's next call to recv() will have an ECONNABORTED error,
+ // preventing the emulator from reading the command that adb has sent.
+ // https://code.google.com/p/android/issues/detail?id=21021
+ int result;
+ do {
+ char buf[BUFSIZ];
+ result = adb_read(fd, buf, sizeof(buf));
+ // Keep reading until zero bytes (EOF) or an error. If 'adb emu kill'
+ // is executed, the emulator calls exit() which causes adb to get
+ // ECONNRESET. Any other emu command is followed by the quit command
+ // that we sent above, and that causes the emulator to close the socket
+ // which should cause zero bytes (EOF) to be returned.
+ } while (result > 0);
+
adb_close(fd);
return 0;
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
new file mode 100644
index 0000000..99ff539
--- /dev/null
+++ b/adb/daemon/main.cpp
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG TRACE_ADB
+
+#include "sysdeps.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <sys/prctl.h>
+
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "cutils/properties.h"
+#include "private/android_filesystem_config.h"
+#include "selinux/selinux.h"
+
+#include "adb.h"
+#include "adb_auth.h"
+#include "adb_listeners.h"
+#include "transport.h"
+#include "qemu_tracing.h"
+
+static const char* root_seclabel = nullptr;
+
+static void drop_capabilities_bounding_set_if_needed() {
+#ifdef ALLOW_ADBD_ROOT
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.debuggable", value, "");
+ if (strcmp(value, "1") == 0) {
+ return;
+ }
+#endif
+ for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
+ if (i == CAP_SETUID || i == CAP_SETGID) {
+ // CAP_SETUID CAP_SETGID needed by /system/bin/run-as
+ continue;
+ }
+
+ int err = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
+
+ // Some kernels don't have file capabilities compiled in, and
+ // prctl(PR_CAPBSET_DROP) returns EINVAL. Don't automatically
+ // die when we see such misconfigured kernels.
+ if ((err < 0) && (errno != EINVAL)) {
+ PLOG(FATAL) << "Could not drop capabilities";
+ }
+ }
+}
+
+static bool should_drop_privileges() {
+#if defined(ALLOW_ADBD_ROOT)
+ char value[PROPERTY_VALUE_MAX];
+
+ // The emulator is never secure, so don't drop privileges there.
+ // TODO: this seems like a bug --- shouldn't the emulator behave like a device?
+ property_get("ro.kernel.qemu", value, "");
+ if (strcmp(value, "1") == 0) {
+ return false;
+ }
+
+ // The properties that affect `adb root` and `adb unroot` are ro.secure and
+ // ro.debuggable. In this context the names don't make the expected behavior
+ // particularly obvious.
+ //
+ // ro.debuggable:
+ // Allowed to become root, but not necessarily the default. Set to 1 on
+ // eng and userdebug builds.
+ //
+ // ro.secure:
+ // Drop privileges by default. Set to 1 on userdebug and user builds.
+ property_get("ro.secure", value, "1");
+ bool ro_secure = (strcmp(value, "1") == 0);
+
+ property_get("ro.debuggable", value, "");
+ bool ro_debuggable = (strcmp(value, "1") == 0);
+
+ // Drop privileges if ro.secure is set...
+ bool drop = ro_secure;
+
+ property_get("service.adb.root", value, "");
+ bool adb_root = (strcmp(value, "1") == 0);
+ bool adb_unroot = (strcmp(value, "0") == 0);
+
+ // ...except "adb root" lets you keep privileges in a debuggable build.
+ if (ro_debuggable && adb_root) {
+ drop = false;
+ }
+
+ // ...and "adb unroot" lets you explicitly drop privileges.
+ if (adb_unroot) {
+ drop = true;
+ }
+
+ return drop;
+#else
+ return true; // "adb root" not allowed, always drop privileges.
+#endif // ALLOW_ADBD_ROOT
+}
+
+int adbd_main(int server_port) {
+ umask(0);
+
+ signal(SIGPIPE, SIG_IGN);
+
+ init_transport_registration();
+
+ // We need to call this even if auth isn't enabled because the file
+ // descriptor will always be open.
+ adbd_cloexec_auth_socket();
+
+ auth_enabled = property_get_bool("ro.adb.secure", 0) != 0;
+ if (auth_enabled) {
+ adbd_auth_init();
+ }
+
+ // Our external storage path may be different than apps, since
+ // we aren't able to bind mount after dropping root.
+ const char* adb_external_storage = getenv("ADB_EXTERNAL_STORAGE");
+ if (adb_external_storage != nullptr) {
+ setenv("EXTERNAL_STORAGE", adb_external_storage, 1);
+ } else {
+ D("Warning: ADB_EXTERNAL_STORAGE is not set. Leaving EXTERNAL_STORAGE"
+ " unchanged.\n");
+ }
+
+ // Add extra groups:
+ // AID_ADB to access the USB driver
+ // AID_LOG to read system logs (adb logcat)
+ // AID_INPUT to diagnose input issues (getevent)
+ // AID_INET to diagnose network issues (ping)
+ // AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
+ // AID_SDCARD_R to allow reading from the SD card
+ // AID_SDCARD_RW to allow writing to the SD card
+ // AID_NET_BW_STATS to read out qtaguid statistics
+ gid_t groups[] = {AID_ADB, AID_LOG, AID_INPUT,
+ AID_INET, AID_NET_BT, AID_NET_BT_ADMIN,
+ AID_SDCARD_R, AID_SDCARD_RW, AID_NET_BW_STATS};
+ if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) != 0) {
+ PLOG(FATAL) << "Could not set supplental groups";
+ }
+
+ /* don't listen on a port (default 5037) if running in secure mode */
+ /* don't run as root if we are running in secure mode */
+ if (should_drop_privileges()) {
+ drop_capabilities_bounding_set_if_needed();
+
+ /* then switch user and group to "shell" */
+ if (setgid(AID_SHELL) != 0) {
+ PLOG(FATAL) << "Could not setgid";
+ }
+ if (setuid(AID_SHELL) != 0) {
+ PLOG(FATAL) << "Could not setuid";
+ }
+
+ D("Local port disabled\n");
+ } else {
+ if ((root_seclabel != nullptr) && (is_selinux_enabled() > 0)) {
+ if (setcon(root_seclabel) < 0) {
+ LOG(FATAL) << "Could not set selinux context";
+ }
+ }
+ std::string local_name =
+ android::base::StringPrintf("tcp:%d", server_port);
+ if (install_listener(local_name, "*smartsocket*", nullptr, 0)) {
+ LOG(FATAL) << "Could not install *smartsocket* listener";
+ }
+ }
+
+ bool is_usb = false;
+ if (access(USB_ADB_PATH, F_OK) == 0 || access(USB_FFS_ADB_EP0, F_OK) == 0) {
+ // Listen on USB.
+ usb_init();
+ is_usb = true;
+ }
+
+ // If one of these properties is set, also listen on that port.
+ // If one of the properties isn't set and we couldn't listen on usb, listen
+ // on the default port.
+ char prop_port[PROPERTY_VALUE_MAX];
+ property_get("service.adb.tcp.port", prop_port, "");
+ if (prop_port[0] == '\0') {
+ property_get("persist.adb.tcp.port", prop_port, "");
+ }
+
+ int port;
+ if (sscanf(prop_port, "%d", &port) == 1 && port > 0) {
+ printf("using port=%d\n", port);
+ // Listen on TCP port specified by service.adb.tcp.port property.
+ local_init(port);
+ } else if (!is_usb) {
+ // Listen on default port.
+ local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
+ }
+
+ D("adbd_main(): pre init_jdwp()\n");
+ init_jdwp();
+ D("adbd_main(): post init_jdwp()\n");
+
+ D("Event loop starting\n");
+ fdevent_loop();
+
+ return 0;
+}
+
+static void close_stdin() {
+ int fd = unix_open("/dev/null", O_RDONLY);
+ if (fd == -1) {
+ perror("failed to open /dev/null, stdin will remain open");
+ return;
+ }
+ dup2(fd, STDIN_FILENO);
+ adb_close(fd);
+}
+
+int main(int argc, char** argv) {
+ android::base::InitLogging(argv);
+
+ while (true) {
+ static struct option opts[] = {
+ {"root_seclabel", required_argument, nullptr, 's'},
+ {"device_banner", required_argument, nullptr, 'b'},
+ {"version", no_argument, nullptr, 'v'},
+ };
+
+ int option_index = 0;
+ int c = getopt_long(argc, argv, "", opts, &option_index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 's':
+ root_seclabel = optarg;
+ break;
+ case 'b':
+ adb_device_banner = optarg;
+ break;
+ case 'v':
+ printf("Android Debug Bridge Daemon version %d.%d.%d %s\n",
+ ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
+ ADB_REVISION);
+ return 0;
+ default:
+ // getopt already prints "adbd: invalid option -- %c" for us.
+ return 1;
+ }
+ }
+
+ close_stdin();
+
+ adb_trace_init();
+
+ /* If adbd runs inside the emulator this will enable adb tracing via
+ * adb-debug qemud service in the emulator. */
+ adb_qemu_trace_init();
+
+ D("Handling main()\n");
+ return adbd_main(DEFAULT_ADB_PORT);
+}
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 45d33db..0c43c5e 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -15,25 +15,23 @@
** limitations under the License.
*/
-#include <sys/ioctl.h>
+#define TRACE_TAG TRACE_FDEVENT
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
+#include "sysdeps.h"
+#include "fdevent.h"
+
#include <errno.h>
-
#include <fcntl.h>
-
#include <stdarg.h>
#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
#include "adb_io.h"
#include "adb_trace.h"
-#include "fdevent.h"
-#include "sysdeps.h"
-
-#define TRACE_TAG TRACE_FDEVENT
/* !!! Do not enable DEBUG for the adb that will run as the server:
** both stdout and stderr are used to communicate between the client
@@ -587,6 +585,7 @@
FATAL("fde %p not created by fdevent_create()\n", fde);
}
fdevent_remove(fde);
+ free(fde);
}
void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
diff --git a/adb/fdevent.h b/adb/fdevent.h
index a8102ca..8d84b29 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -19,10 +19,6 @@
#include <stdint.h> /* for int64_t */
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/* events that may be observed */
#define FDE_READ 0x0001
#define FDE_WRITE 0x0002
@@ -32,7 +28,7 @@
/* features that may be set (via the events set/add/del interface) */
#define FDE_DONT_CLOSE 0x0080
-typedef struct fdevent fdevent;
+struct fdevent;
typedef void (*fd_func)(int fd, unsigned events, void *userdata);
@@ -82,8 +78,4 @@
void *arg;
};
-#ifdef __cplusplus
-}
-#endif
-
#endif
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 4ba730b..2efc890 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -79,8 +79,7 @@
fflush(stderr);
}
-void sync_quit(int fd)
-{
+static void sync_quit(int fd) {
syncmsg msg;
msg.req.id = ID_QUIT;
@@ -91,8 +90,7 @@
typedef void (*sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char *name, void *cookie);
-int sync_ls(int fd, const char *path, sync_ls_cb func, void *cookie)
-{
+static int sync_ls(int fd, const char* path, sync_ls_cb func, void* cookie) {
syncmsg msg;
char buf[257];
int len;
@@ -130,8 +128,6 @@
return -1;
}
-typedef struct syncsendbuf syncsendbuf;
-
struct syncsendbuf {
unsigned id;
unsigned size;
@@ -140,9 +136,7 @@
static syncsendbuf send_buffer;
-int sync_readtime(int fd, const char *path, unsigned int *timestamp,
- unsigned int *mode)
-{
+static int sync_readtime(int fd, const char* path, unsigned int* timestamp, unsigned int* mode) {
syncmsg msg;
int len = strlen(path);
@@ -201,8 +195,7 @@
return 0;
}
-int sync_readmode(int fd, const char *path, unsigned *mode)
-{
+static int sync_readmode(int fd, const char* path, unsigned* mode) {
syncmsg msg;
int len = strlen(path);
@@ -421,8 +414,7 @@
return 0;
}
-int sync_recv(int fd, const char *rpath, const char *lpath, int show_progress)
-{
+static int sync_recv(int fd, const char* rpath, const char* lpath, int show_progress) {
syncmsg msg;
int len;
int lfd = -1;
@@ -541,11 +533,11 @@
printf("%08x %08x %08x %s\n", mode, size, time, name);
}
-int do_sync_ls(const char *path)
-{
- int fd = adb_connect("sync:");
- if(fd < 0) {
- fprintf(stderr,"error: %s\n", adb_error());
+int do_sync_ls(const char* path) {
+ std::string error;
+ int fd = adb_connect("sync:", &error);
+ if (fd < 0) {
+ fprintf(stderr,"error: %s\n", error.c_str());
return 1;
}
@@ -557,8 +549,6 @@
}
}
-typedef struct copyinfo copyinfo;
-
struct copyinfo
{
copyinfo *next;
@@ -570,17 +560,14 @@
int flag;
};
-copyinfo *mkcopyinfo(const char *spath, const char *dpath,
- const char *name, int isdir)
-{
+static copyinfo* mkcopyinfo(const char* spath, const char* dpath, const char* name, int isdir) {
int slen = strlen(spath);
int dlen = strlen(dpath);
int nlen = strlen(name);
int ssize = slen + nlen + 2;
int dsize = dlen + nlen + 2;
- copyinfo *ci = reinterpret_cast<copyinfo*>(
- malloc(sizeof(copyinfo) + ssize + dsize));
+ copyinfo *ci = reinterpret_cast<copyinfo*>(malloc(sizeof(copyinfo) + ssize + dsize));
if(ci == 0) {
fprintf(stderr,"out of memory\n");
abort();
@@ -747,11 +734,11 @@
{
struct stat st;
unsigned mode;
- int fd;
- fd = adb_connect("sync:");
- if(fd < 0) {
- fprintf(stderr,"error: %s\n", adb_error());
+ std::string error;
+ int fd = adb_connect("sync:", &error);
+ if (fd < 0) {
+ fprintf(stderr,"error: %s\n", error.c_str());
return 1;
}
@@ -804,16 +791,15 @@
}
-typedef struct {
+struct sync_ls_build_list_cb_args {
copyinfo **filelist;
copyinfo **dirlist;
const char *rpath;
const char *lpath;
-} sync_ls_build_list_cb_args;
+};
-void
-sync_ls_build_list_cb(unsigned mode, unsigned size, unsigned time,
- const char *name, void *cookie)
+static void sync_ls_build_list_cb(unsigned mode, unsigned size, unsigned time,
+ const char* name, void* cookie)
{
sync_ls_build_list_cb_args *args = (sync_ls_build_list_cb_args *)cookie;
copyinfo *ci;
@@ -971,11 +957,10 @@
unsigned mode, time;
struct stat st;
- int fd;
-
- fd = adb_connect("sync:");
- if(fd < 0) {
- fprintf(stderr,"error: %s\n", adb_error());
+ std::string error;
+ int fd = adb_connect("sync:", &error);
+ if (fd < 0) {
+ fprintf(stderr,"error: %s\n", error.c_str());
return 1;
}
@@ -1031,18 +1016,19 @@
}
}
-int do_sync_sync(const char *lpath, const char *rpath, int listonly)
+int do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only)
{
- fprintf(stderr,"syncing %s...\n",rpath);
+ fprintf(stderr, "syncing %s...\n", rpath.c_str());
- int fd = adb_connect("sync:");
- if(fd < 0) {
- fprintf(stderr,"error: %s\n", adb_error());
+ std::string error;
+ int fd = adb_connect("sync:", &error);
+ if (fd < 0) {
+ fprintf(stderr, "error: %s\n", error.c_str());
return 1;
}
BEGIN();
- if(copy_local_dir_remote(fd, lpath, rpath, 1, listonly)){
+ if (copy_local_dir_remote(fd, lpath.c_str(), rpath.c_str(), 1, list_only)) {
return 1;
} else {
END();
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index ac01678..e8e9a0f 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -14,6 +14,11 @@
* limitations under the License.
*/
+#define TRACE_TAG TRACE_SYNC
+
+#include "sysdeps.h"
+#include "file_sync_service.h"
+
#include <dirent.h>
#include <errno.h>
#include <selinux/android.h>
@@ -25,12 +30,8 @@
#include <unistd.h>
#include <utime.h>
-#include "sysdeps.h"
-
-#define TRACE_TAG TRACE_SYNC
#include "adb.h"
#include "adb_io.h"
-#include "file_sync_service.h"
#include "private/android_filesystem_config.h"
static bool should_use_fs_config(const char* path) {
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index 5b69a63..344eb98 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -17,9 +17,7 @@
#ifndef _FILE_SYNC_SERVICE_H_
#define _FILE_SYNC_SERVICE_H_
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include <string>
#define htoll(x) (x)
#define ltohl(x) (x)
@@ -38,7 +36,7 @@
#define ID_FAIL MKID('F','A','I','L')
#define ID_QUIT MKID('Q','U','I','T')
-typedef union {
+union syncmsg {
unsigned id;
struct {
unsigned id;
@@ -65,19 +63,15 @@
unsigned id;
unsigned msglen;
} status;
-} syncmsg;
+} ;
void file_sync_service(int fd, void *cookie);
int do_sync_ls(const char *path);
int do_sync_push(const char *lpath, const char *rpath, int show_progress);
-int do_sync_sync(const char *lpath, const char *rpath, int listonly);
+int do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only);
int do_sync_pull(const char *rpath, const char *lpath, int show_progress, int pullTime);
#define SYNC_DATA_MAX (64*1024)
-#ifdef __cplusplus
-}
-#endif
-
#endif
diff --git a/adb/get_my_path_darwin.c b/adb/get_my_path_darwin.cpp
similarity index 100%
rename from adb/get_my_path_darwin.c
rename to adb/get_my_path_darwin.cpp
diff --git a/adb/jdwp_service.cpp b/adb/jdwp_service.cpp
index f0b4ba7..c0f7ec2 100644
--- a/adb/jdwp_service.cpp
+++ b/adb/jdwp_service.cpp
@@ -1,12 +1,32 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
/* implement the "debug-ports" and "track-debug-ports" device services */
+
+#define TRACE_TAG TRACE_JDWP
+
#include "sysdeps.h"
-#define TRACE_TAG TRACE_JDWP
-#include "adb.h"
+
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
+#include "adb.h"
+
/* here's how these things work.
when adbd starts, it creates a unix server socket
@@ -101,7 +121,6 @@
#include <sys/socket.h>
#include <sys/un.h>
-typedef struct JdwpProcess JdwpProcess;
struct JdwpProcess {
JdwpProcess* next;
JdwpProcess* prev;
@@ -435,11 +454,10 @@
#define JDWP_CONTROL_NAME "\0jdwp-control"
#define JDWP_CONTROL_NAME_LEN (sizeof(JDWP_CONTROL_NAME)-1)
-typedef struct {
+struct JdwpControl {
int listen_socket;
fdevent* fde;
-
-} JdwpControl;
+};
static void
@@ -550,10 +568,10 @@
** this simply returns the list of known JDWP process pids
**/
-typedef struct {
+struct JdwpSocket {
asocket socket;
int pass;
-} JdwpSocket;
+};
static void
jdwp_socket_close( asocket* s )
@@ -622,8 +640,6 @@
** to the client...
**/
-typedef struct JdwpTracker JdwpTracker;
-
struct JdwpTracker {
asocket socket;
JdwpTracker* next;
@@ -734,4 +750,3 @@
}
#endif /* !ADB_HOST */
-
diff --git a/adb/qemu_tracing.h b/adb/qemu_tracing.h
index bf80457..ff42d4f 100644
--- a/adb/qemu_tracing.h
+++ b/adb/qemu_tracing.h
@@ -21,16 +21,8 @@
#ifndef __QEMU_TRACING_H
#define __QEMU_TRACING_H
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/* Initializes connection with the adb-debug qemud service in the emulator. */
int adb_qemu_trace_init(void);
void adb_qemu_trace(const char* fmt, ...);
-#ifdef __cplusplus
-}
-#endif
-
#endif /* __QEMU_TRACING_H */
diff --git a/adb/remount_service.cpp b/adb/remount_service.cpp
index a83d5b1..7a3b89a 100644
--- a/adb/remount_service.cpp
+++ b/adb/remount_service.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#define TRACE_TAG TRACE_ADB
+
+#include "sysdeps.h"
+
#include <errno.h>
#include <fcntl.h>
#include <mntent.h>
@@ -25,126 +29,94 @@
#include <string>
-#include "sysdeps.h"
-
-#define TRACE_TAG TRACE_ADB
#include "adb.h"
#include "adb_io.h"
+#include "adb_utils.h"
#include "cutils/properties.h"
-static int system_ro = 1;
-static int vendor_ro = 1;
-static int oem_ro = 1;
-
-/* Returns the device used to mount a directory in /proc/mounts */
-static std::string find_mount(const char *dir) {
- FILE* fp;
- struct mntent* mentry;
- char* device = NULL;
-
- if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
- return NULL;
+// Returns the device used to mount a directory in /proc/mounts.
+static std::string find_mount(const char* dir) {
+ std::unique_ptr<FILE, int(*)(FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
+ if (!fp) {
+ return "";
}
- while ((mentry = getmntent(fp)) != NULL) {
- if (strcmp(dir, mentry->mnt_dir) == 0) {
- device = mentry->mnt_fsname;
- break;
+
+ mntent* e;
+ while ((e = getmntent(fp.get())) != nullptr) {
+ if (strcmp(dir, e->mnt_dir) == 0) {
+ return e->mnt_fsname;
}
}
- endmntent(fp);
- return device;
+ return "";
}
-static bool has_partition(const char* path) {
- struct stat sb;
- return (lstat(path, &sb) == 0 && S_ISDIR(sb.st_mode));
-}
-
-int make_block_device_writable(const std::string& dev) {
+bool make_block_device_writable(const std::string& dev) {
int fd = unix_open(dev.c_str(), O_RDONLY | O_CLOEXEC);
if (fd == -1) {
- return -1;
+ return false;
}
- int result = -1;
int OFF = 0;
- if (!ioctl(fd, BLKROSET, &OFF)) {
- result = 0;
- }
+ bool result = (ioctl(fd, BLKROSET, &OFF) != -1);
adb_close(fd);
-
return result;
}
-// Init mounts /system as read only, remount to enable writes.
-static int remount(const char* dir, int* dir_ro) {
- std::string dev(find_mount(dir));
- if (dev.empty() || make_block_device_writable(dev)) {
- return -1;
+static bool remount_partition(int fd, const char* dir) {
+ if (!directory_exists(dir)) {
+ return true;
}
-
- int rc = mount(dev.c_str(), dir, "none", MS_REMOUNT, NULL);
- *dir_ro = rc;
- return rc;
-}
-
-static bool remount_partition(int fd, const char* partition, int* ro) {
- if (!has_partition(partition)) {
+ std::string dev = find_mount(dir);
+ if (dev.empty()) {
+ return true;
+ }
+ if (!make_block_device_writable(dev)) {
+ WriteFdFmt(fd, "remount of %s failed; couldn't make block device %s writable: %s\n",
+ dir, dev.c_str(), strerror(errno));
+ return false;
+ }
+ if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) {
+ WriteFdFmt(fd, "remount of %s failed: %s\n", dir, strerror(errno));
+ return false;
+ }
return true;
- }
- if (remount(partition, ro)) {
- char buf[200];
- snprintf(buf, sizeof(buf), "remount of %s failed: %s\n", partition, strerror(errno));
- WriteStringFully(fd, buf);
- return false;
- }
- return true;
}
void remount_service(int fd, void* cookie) {
- char prop_buf[PROPERTY_VALUE_MAX];
-
if (getuid() != 0) {
- WriteStringFully(fd, "Not running as root. Try \"adb root\" first.\n");
+ WriteFdExactly(fd, "Not running as root. Try \"adb root\" first.\n");
adb_close(fd);
return;
}
- bool system_verified = false, vendor_verified = false;
- property_get("partition.system.verified", prop_buf, "0");
- if (!strcmp(prop_buf, "1")) {
- system_verified = true;
- }
+ char prop_buf[PROPERTY_VALUE_MAX];
+ property_get("partition.system.verified", prop_buf, "");
+ bool system_verified = (strlen(prop_buf) > 0);
- property_get("partition.vendor.verified", prop_buf, "0");
- if (!strcmp(prop_buf, "1")) {
- vendor_verified = true;
- }
+ property_get("partition.vendor.verified", prop_buf, "");
+ bool vendor_verified = (strlen(prop_buf) > 0);
if (system_verified || vendor_verified) {
// Allow remount but warn of likely bad effects
bool both = system_verified && vendor_verified;
- char buffer[200];
- snprintf(buffer, sizeof(buffer),
- "dm_verity is enabled on the %s%s%s partition%s.\n",
- system_verified ? "system" : "",
- both ? " and " : "",
- vendor_verified ? "vendor" : "",
- both ? "s" : "");
- WriteStringFully(fd, buffer);
- snprintf(buffer, sizeof(buffer),
- "Use \"adb disable-verity\" to disable verity.\n"
- "If you do not, remount may succeed, however, you will still "
- "not be able to write to these volumes.\n");
- WriteStringFully(fd, buffer);
+ WriteFdFmt(fd,
+ "dm_verity is enabled on the %s%s%s partition%s.\n",
+ system_verified ? "system" : "",
+ both ? " and " : "",
+ vendor_verified ? "vendor" : "",
+ both ? "s" : "");
+ WriteFdExactly(fd,
+ "Use \"adb disable-verity\" to disable verity.\n"
+ "If you do not, remount may succeed, however, you will still "
+ "not be able to write to these volumes.\n");
}
bool success = true;
- success &= remount_partition(fd, "/system", &system_ro);
- success &= remount_partition(fd, "/vendor", &vendor_ro);
- success &= remount_partition(fd, "/oem", &oem_ro);
+ success &= remount_partition(fd, "/system");
+ success &= remount_partition(fd, "/vendor");
+ success &= remount_partition(fd, "/oem");
- WriteStringFully(fd, success ? "remount succeeded\n" : "remount failed\n");
+ WriteFdExactly(fd, success ? "remount succeeded\n" : "remount failed\n");
adb_close(fd);
}
diff --git a/adb/remount_service.h b/adb/remount_service.h
index e1763cf..7bda1be 100644
--- a/adb/remount_service.h
+++ b/adb/remount_service.h
@@ -19,7 +19,7 @@
#include <string>
-int make_block_device_writable(const std::string&);
+bool make_block_device_writable(const std::string&);
void remount_service(int, void*);
#endif
diff --git a/adb/services.cpp b/adb/services.cpp
index e7bf6b0..b869479 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#define TRACE_TAG TRACE_SERVICES
+
+#include "sysdeps.h"
+
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
@@ -27,22 +31,21 @@
#include <unistd.h>
#endif
+#include <base/file.h>
+#include <base/stringprintf.h>
+#include <base/strings.h>
+
#if !ADB_HOST
#include "cutils/android_reboot.h"
#include "cutils/properties.h"
#endif
-#include "sysdeps.h"
-
-#define TRACE_TAG TRACE_SERVICES
#include "adb.h"
#include "adb_io.h"
#include "file_sync_service.h"
#include "remount_service.h"
#include "transport.h"
-typedef struct stinfo stinfo;
-
struct stinfo {
void (*func)(int fd, void *cookie);
int fd;
@@ -60,102 +63,119 @@
#if !ADB_HOST
-void restart_root_service(int fd, void *cookie)
-{
- char buf[100];
- char value[PROPERTY_VALUE_MAX];
-
+void restart_root_service(int fd, void *cookie) {
if (getuid() == 0) {
- snprintf(buf, sizeof(buf), "adbd is already running as root\n");
- WriteFdExactly(fd, buf, strlen(buf));
+ WriteFdExactly(fd, "adbd is already running as root\n");
adb_close(fd);
} else {
+ char value[PROPERTY_VALUE_MAX];
property_get("ro.debuggable", value, "");
if (strcmp(value, "1") != 0) {
- snprintf(buf, sizeof(buf), "adbd cannot run as root in production builds\n");
- WriteFdExactly(fd, buf, strlen(buf));
+ WriteFdExactly(fd, "adbd cannot run as root in production builds\n");
adb_close(fd);
return;
}
property_set("service.adb.root", "1");
- snprintf(buf, sizeof(buf), "restarting adbd as root\n");
- WriteFdExactly(fd, buf, strlen(buf));
+ WriteFdExactly(fd, "restarting adbd as root\n");
adb_close(fd);
}
}
-void restart_unroot_service(int fd, void *cookie)
-{
- char buf[100];
-
+void restart_unroot_service(int fd, void *cookie) {
if (getuid() != 0) {
- snprintf(buf, sizeof(buf), "adbd not running as root\n");
- WriteFdExactly(fd, buf, strlen(buf));
+ WriteFdExactly(fd, "adbd not running as root\n");
adb_close(fd);
} else {
property_set("service.adb.root", "0");
- snprintf(buf, sizeof(buf), "restarting adbd as non root\n");
- WriteFdExactly(fd, buf, strlen(buf));
+ WriteFdExactly(fd, "restarting adbd as non root\n");
adb_close(fd);
}
}
-void restart_tcp_service(int fd, void *cookie)
-{
- char buf[100];
- char value[PROPERTY_VALUE_MAX];
+void restart_tcp_service(int fd, void *cookie) {
int port = (int) (uintptr_t) cookie;
-
if (port <= 0) {
- snprintf(buf, sizeof(buf), "invalid port\n");
- WriteFdExactly(fd, buf, strlen(buf));
+ WriteFdFmt(fd, "invalid port %d\n", port);
adb_close(fd);
return;
}
+ char value[PROPERTY_VALUE_MAX];
snprintf(value, sizeof(value), "%d", port);
property_set("service.adb.tcp.port", value);
- snprintf(buf, sizeof(buf), "restarting in TCP mode port: %d\n", port);
- WriteFdExactly(fd, buf, strlen(buf));
+ WriteFdFmt(fd, "restarting in TCP mode port: %d\n", port);
adb_close(fd);
}
-void restart_usb_service(int fd, void *cookie)
-{
- char buf[100];
-
+void restart_usb_service(int fd, void *cookie) {
property_set("service.adb.tcp.port", "0");
- snprintf(buf, sizeof(buf), "restarting in USB mode\n");
- WriteFdExactly(fd, buf, strlen(buf));
+ WriteFdExactly(fd, "restarting in USB mode\n");
adb_close(fd);
}
-void reboot_service(int fd, void *arg)
-{
- char buf[100];
- char property_val[PROPERTY_VALUE_MAX];
- int ret;
+static bool reboot_service_impl(int fd, const char* arg) {
+ const char* reboot_arg = arg;
+ bool auto_reboot = false;
+
+ if (strcmp(reboot_arg, "sideload-auto-reboot") == 0) {
+ auto_reboot = true;
+ reboot_arg = "sideload";
+ }
+
+ // It reboots into sideload mode by setting "--sideload" or "--sideload_auto_reboot"
+ // in the command file.
+ if (strcmp(reboot_arg, "sideload") == 0) {
+ if (getuid() != 0) {
+ WriteFdExactly(fd, "'adb root' is required for 'adb reboot sideload'.\n");
+ return false;
+ }
+
+ const char* const recovery_dir = "/cache/recovery";
+ const char* const command_file = "/cache/recovery/command";
+ // Ensure /cache/recovery exists.
+ if (adb_mkdir(recovery_dir, 0770) == -1 && errno != EEXIST) {
+ D("Failed to create directory '%s': %s\n", recovery_dir, strerror(errno));
+ return false;
+ }
+
+ bool write_status = android::base::WriteStringToFile(
+ auto_reboot ? "--sideload_auto_reboot" : "--sideload", command_file);
+ if (!write_status) {
+ return false;
+ }
+
+ reboot_arg = "recovery";
+ }
sync();
- ret = snprintf(property_val, sizeof(property_val), "reboot,%s", (char *) arg);
- if (ret >= (int) sizeof(property_val)) {
- snprintf(buf, sizeof(buf), "reboot string too long. length=%d\n", ret);
- WriteFdExactly(fd, buf, strlen(buf));
- goto cleanup;
+ char property_val[PROPERTY_VALUE_MAX];
+ int ret = snprintf(property_val, sizeof(property_val), "reboot,%s", reboot_arg);
+ if (ret >= static_cast<int>(sizeof(property_val))) {
+ WriteFdFmt(fd, "reboot string too long: %d\n", ret);
+ return false;
}
ret = property_set(ANDROID_RB_PROPERTY, property_val);
if (ret < 0) {
- snprintf(buf, sizeof(buf), "reboot failed: %d\n", ret);
- WriteFdExactly(fd, buf, strlen(buf));
- goto cleanup;
+ WriteFdFmt(fd, "reboot failed: %d\n", ret);
+ return false;
}
- // Don't return early. Give the reboot command time to take effect
- // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
- while(1) { pause(); }
-cleanup:
+
+ return true;
+}
+
+void reboot_service(int fd, void* arg)
+{
+ if (reboot_service_impl(fd, static_cast<const char*>(arg))) {
+ // Don't return early. Give the reboot command time to take effect
+ // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
+ while (true) {
+ pause();
+ }
+ }
+
free(arg);
adb_close(fd);
}
@@ -165,7 +185,7 @@
const char* command = reinterpret_cast<const char*>(arg);
if (handle_forward_request(command, kTransportAny, NULL, fd) < 0) {
- sendfailmsg(fd, "not a reverse forwarding command");
+ SendFail(fd, "not a reverse forwarding command");
}
free(arg);
adb_close(fd);
@@ -190,8 +210,7 @@
sti->cookie = cookie;
sti->fd = s[1];
- adb_thread_t t;
- if (adb_thread_create(&t, service_bootstrap_func, sti)) {
+ if (!adb_thread_create(service_bootstrap_func, sti)) {
free(sti);
adb_close(s[0]);
adb_close(s[1]);
@@ -332,7 +351,7 @@
pid_t pid = (pid_t) (uintptr_t) cookie;
D("entered. fd=%d of pid=%d\n", fd, pid);
- for (;;) {
+ while (true) {
int status;
pid_t p = waitpid(pid, &status, 0);
if (p == pid) {
@@ -358,12 +377,7 @@
}
}
-static int create_subproc_thread(const char *name, const subproc_mode mode)
-{
- adb_thread_t t;
- int ret_fd;
- pid_t pid = -1;
-
+static int create_subproc_thread(const char *name, bool pty = false) {
const char *arg0, *arg1;
if (name == 0 || *name == 0) {
arg0 = "-"; arg1 = 0;
@@ -371,16 +385,12 @@
arg0 = "-c"; arg1 = name;
}
- switch (mode) {
- case SUBPROC_PTY:
+ pid_t pid = -1;
+ int ret_fd;
+ if (pty) {
ret_fd = create_subproc_pty(SHELL_COMMAND, arg0, arg1, &pid);
- break;
- case SUBPROC_RAW:
+ } else {
ret_fd = create_subproc_raw(SHELL_COMMAND, arg0, arg1, &pid);
- break;
- default:
- fprintf(stderr, "invalid subproc_mode %d\n", mode);
- return -1;
}
D("create_subproc ret_fd=%d pid=%d\n", ret_fd, pid);
@@ -390,7 +400,7 @@
sti->cookie = (void*) (uintptr_t) pid;
sti->fd = ret_fd;
- if (adb_thread_create(&t, service_bootstrap_func, sti)) {
+ if (!adb_thread_create(service_bootstrap_func, sti)) {
free(sti);
adb_close(ret_fd);
fprintf(stderr, "cannot create service thread\n");
@@ -442,9 +452,9 @@
} else if (!strncmp(name, "jdwp:", 5)) {
ret = create_jdwp_connection_fd(atoi(name+5));
} else if(!HOST && !strncmp(name, "shell:", 6)) {
- ret = create_subproc_thread(name + 6, SUBPROC_PTY);
+ ret = create_subproc_thread(name + 6, true);
} else if(!HOST && !strncmp(name, "exec:", 5)) {
- ret = create_subproc_thread(name + 5, SUBPROC_RAW);
+ ret = create_subproc_thread(name + 5);
} else if(!strncmp(name, "sync:", 5)) {
ret = create_service_thread(file_sync_service, NULL);
} else if(!strncmp(name, "remount:", 8)) {
@@ -458,21 +468,10 @@
} else if(!strncmp(name, "unroot:", 7)) {
ret = create_service_thread(restart_unroot_service, NULL);
} else if(!strncmp(name, "backup:", 7)) {
- char* arg = strdup(name + 7);
- if (arg == NULL) return -1;
- char* c = arg;
- for (; *c != '\0'; c++) {
- if (*c == ':')
- *c = ' ';
- }
- char* cmd;
- if (asprintf(&cmd, "/system/bin/bu backup %s", arg) != -1) {
- ret = create_subproc_thread(cmd, SUBPROC_RAW);
- free(cmd);
- }
- free(arg);
+ ret = create_subproc_thread(android::base::StringPrintf("/system/bin/bu backup %s",
+ (name + 7)).c_str());
} else if(!strncmp(name, "restore:", 8)) {
- ret = create_subproc_thread("/system/bin/bu restore", SUBPROC_RAW);
+ ret = create_subproc_thread("/system/bin/bu restore");
} else if(!strncmp(name, "tcpip:", 6)) {
int port;
if (sscanf(name + 6, "%d", &port) != 1) {
@@ -505,7 +504,7 @@
#if ADB_HOST
struct state_info {
- transport_type transport;
+ TransportType transport_type;
char* serial;
int state;
};
@@ -516,12 +515,13 @@
D("wait_for_state %d\n", sinfo->state);
- const char* err = "unknown error";
- atransport *t = acquire_one_transport(sinfo->state, sinfo->transport, sinfo->serial, &err);
- if(t != 0) {
- WriteFdExactly(fd, "OKAY", 4);
+ std::string error_msg = "unknown error";
+ atransport* t = acquire_one_transport(sinfo->state, sinfo->transport_type, sinfo->serial,
+ &error_msg);
+ if (t != 0) {
+ SendOkay(fd);
} else {
- sendfailmsg(fd, err);
+ SendFail(fd, error_msg);
}
if (sinfo->serial)
@@ -531,35 +531,31 @@
D("wait_for_state is done\n");
}
-static void connect_device(char* host, char* buffer, int buffer_size)
-{
- int port, fd;
- char* portstr = strchr(host, ':');
- char hostbuf[100];
- char serial[100];
- int ret;
-
- strncpy(hostbuf, host, sizeof(hostbuf) - 1);
- if (portstr) {
- if (portstr - host >= (ptrdiff_t)sizeof(hostbuf)) {
- snprintf(buffer, buffer_size, "bad host name %s", host);
- return;
- }
- // zero terminate the host at the point we found the colon
- hostbuf[portstr - host] = 0;
- if (sscanf(portstr + 1, "%d", &port) != 1) {
- snprintf(buffer, buffer_size, "bad port number %s", portstr);
- return;
- }
- } else {
- port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
+static void connect_device(const std::string& host, std::string* response) {
+ if (host.empty()) {
+ *response = "empty host name";
+ return;
}
- snprintf(serial, sizeof(serial), "%s:%d", hostbuf, port);
+ std::vector<std::string> pieces = android::base::Split(host, ":");
+ const std::string& hostname = pieces[0];
- fd = socket_network_client_timeout(hostbuf, port, SOCK_STREAM, 10);
+ int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
+ if (pieces.size() > 1) {
+ if (sscanf(pieces[1].c_str(), "%d", &port) != 1) {
+ *response = android::base::StringPrintf("bad port number %s", pieces[1].c_str());
+ return;
+ }
+ }
+
+ // This may look like we're putting 'host' back together,
+ // but we're actually inserting the default port if necessary.
+ std::string serial = android::base::StringPrintf("%s:%d", hostname.c_str(), port);
+
+ int fd = socket_network_client_timeout(hostname.c_str(), port, SOCK_STREAM, 10);
if (fd < 0) {
- snprintf(buffer, buffer_size, "unable to connect to %s:%d", host, port);
+ *response = android::base::StringPrintf("unable to connect to %s:%d",
+ hostname.c_str(), port);
return;
}
@@ -567,85 +563,73 @@
close_on_exec(fd);
disable_tcp_nagle(fd);
- ret = register_socket_transport(fd, serial, port, 0);
+ int ret = register_socket_transport(fd, serial.c_str(), port, 0);
if (ret < 0) {
adb_close(fd);
- snprintf(buffer, buffer_size, "already connected to %s", serial);
+ *response = android::base::StringPrintf("already connected to %s", serial.c_str());
} else {
- snprintf(buffer, buffer_size, "connected to %s", serial);
+ *response = android::base::StringPrintf("connected to %s", serial.c_str());
}
}
-void connect_emulator(char* port_spec, char* buffer, int buffer_size)
-{
- char* port_separator = strchr(port_spec, ',');
- if (!port_separator) {
- snprintf(buffer, buffer_size,
- "unable to parse '%s' as <console port>,<adb port>",
- port_spec);
+void connect_emulator(const std::string& port_spec, std::string* response) {
+ std::vector<std::string> pieces = android::base::Split(port_spec, ",");
+ if (pieces.size() != 2) {
+ *response = android::base::StringPrintf("unable to parse '%s' as <console port>,<adb port>",
+ port_spec.c_str());
return;
}
- // Zero-terminate console port and make port_separator point to 2nd port.
- *port_separator++ = 0;
- int console_port = strtol(port_spec, NULL, 0);
- int adb_port = strtol(port_separator, NULL, 0);
- if (!(console_port > 0 && adb_port > 0)) {
- *(port_separator - 1) = ',';
- snprintf(buffer, buffer_size,
- "Invalid port numbers: Expected positive numbers, got '%s'",
- port_spec);
+ int console_port = strtol(pieces[0].c_str(), NULL, 0);
+ int adb_port = strtol(pieces[1].c_str(), NULL, 0);
+ if (console_port <= 0 || adb_port <= 0) {
+ *response = android::base::StringPrintf("Invalid port numbers: %s", port_spec.c_str());
return;
}
- /* Check if the emulator is already known.
- * Note: There's a small but harmless race condition here: An emulator not
- * present just yet could be registered by another invocation right
- * after doing this check here. However, local_connect protects
- * against double-registration too. From here, a better error message
- * can be produced. In the case of the race condition, the very specific
- * error message won't be shown, but the data doesn't get corrupted. */
+ // Check if the emulator is already known.
+ // Note: There's a small but harmless race condition here: An emulator not
+ // present just yet could be registered by another invocation right
+ // after doing this check here. However, local_connect protects
+ // against double-registration too. From here, a better error message
+ // can be produced. In the case of the race condition, the very specific
+ // error message won't be shown, but the data doesn't get corrupted.
atransport* known_emulator = find_emulator_transport_by_adb_port(adb_port);
- if (known_emulator != NULL) {
- snprintf(buffer, buffer_size,
- "Emulator on port %d already registered.", adb_port);
+ if (known_emulator != nullptr) {
+ *response = android::base::StringPrintf("Emulator already registered on port %d", adb_port);
return;
}
- /* Check if more emulators can be registered. Similar unproblematic
- * race condition as above. */
+ // Check if more emulators can be registered. Similar unproblematic
+ // race condition as above.
int candidate_slot = get_available_local_transport_index();
if (candidate_slot < 0) {
- snprintf(buffer, buffer_size, "Cannot accept more emulators.");
+ *response = "Cannot accept more emulators";
return;
}
- /* Preconditions met, try to connect to the emulator. */
+ // Preconditions met, try to connect to the emulator.
if (!local_connect_arbitrary_ports(console_port, adb_port)) {
- snprintf(buffer, buffer_size,
- "Connected to emulator on ports %d,%d", console_port, adb_port);
+ *response = android::base::StringPrintf("Connected to emulator on ports %d,%d",
+ console_port, adb_port);
} else {
- snprintf(buffer, buffer_size,
- "Could not connect to emulator on ports %d,%d",
- console_port, adb_port);
+ *response = android::base::StringPrintf("Could not connect to emulator on ports %d,%d",
+ console_port, adb_port);
}
}
-static void connect_service(int fd, void* cookie)
-{
- char buf[4096];
- char resp[4096];
- char *host = reinterpret_cast<char*>(cookie);
-
+static void connect_service(int fd, void* data) {
+ char* host = reinterpret_cast<char*>(data);
+ std::string response;
if (!strncmp(host, "emu:", 4)) {
- connect_emulator(host + 4, buf, sizeof(buf));
+ connect_emulator(host + 4, &response);
} else {
- connect_device(host, buf, sizeof(buf));
+ connect_device(host, &response);
}
+ free(host);
// Send response for emulator and device
- snprintf(resp, sizeof(resp), "%04x%s",(unsigned)strlen(buf), buf);
- WriteFdExactly(fd, resp, strlen(resp));
+ SendProtocolString(fd, response);
adb_close(fd);
}
#endif
@@ -657,6 +641,10 @@
return create_device_tracker();
} else if (!strncmp(name, "wait-for-", strlen("wait-for-"))) {
auto sinfo = reinterpret_cast<state_info*>(malloc(sizeof(state_info)));
+ if (sinfo == nullptr) {
+ fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
+ return NULL;
+ }
if (serial)
sinfo->serial = strdup(serial);
@@ -666,15 +654,18 @@
name += strlen("wait-for-");
if (!strncmp(name, "local", strlen("local"))) {
- sinfo->transport = kTransportLocal;
+ sinfo->transport_type = kTransportLocal;
sinfo->state = CS_DEVICE;
} else if (!strncmp(name, "usb", strlen("usb"))) {
- sinfo->transport = kTransportUsb;
+ sinfo->transport_type = kTransportUsb;
sinfo->state = CS_DEVICE;
} else if (!strncmp(name, "any", strlen("any"))) {
- sinfo->transport = kTransportAny;
+ sinfo->transport_type = kTransportAny;
sinfo->state = CS_DEVICE;
} else {
+ if (sinfo->serial) {
+ free(sinfo->serial);
+ }
free(sinfo);
return NULL;
}
@@ -682,8 +673,8 @@
int fd = create_service_thread(wait_for_state, sinfo);
return create_local_socket(fd);
} else if (!strncmp(name, "connect:", 8)) {
- const char *host = name + 8;
- int fd = create_service_thread(connect_service, (void *)host);
+ char* host = strdup(name + 8);
+ int fd = create_service_thread(connect_service, host);
return create_local_socket(fd);
}
return NULL;
diff --git a/adb/set_verity_enable_state_service.cpp b/adb/set_verity_enable_state_service.cpp
index 139b074..bae38cf 100644
--- a/adb/set_verity_enable_state_service.cpp
+++ b/adb/set_verity_enable_state_service.cpp
@@ -14,20 +14,23 @@
* limitations under the License.
*/
+#define TRACE_TAG TRACE_ADB
+
+#include "sysdeps.h"
+
#include <fcntl.h>
#include <inttypes.h>
#include <stdarg.h>
-#include <stdbool.h>
#include <stdio.h>
#include <sys/stat.h>
-#define TRACE_TAG TRACE_ADB
-#include "adb.h"
#include "cutils/properties.h"
+
+#include "adb.h"
+#include "adb_io.h"
#include "ext4_sb.h"
#include "fs_mgr.h"
#include "remount_service.h"
-#include "sysdeps.h"
#define FSTAB_PREFIX "/fstab."
struct fstab *fstab;
@@ -38,18 +41,6 @@
static const bool kAllowDisableVerity = false;
#endif
-__attribute__((__format__(printf, 2, 3))) __nonnull((2))
-static void write_console(int fd, const char* format, ...)
-{
- char buffer[256];
- va_list args;
- va_start (args, format);
- vsnprintf (buffer, sizeof(buffer), format, args);
- va_end (args);
-
- adb_write(fd, buffer, strnlen(buffer, sizeof(buffer)));
-}
-
static int get_target_device_size(int fd, const char *blk_device,
uint64_t *device_size)
{
@@ -61,18 +52,18 @@
data_device = adb_open(blk_device, O_RDONLY | O_CLOEXEC);
if (data_device < 0) {
- write_console(fd, "Error opening block device (%s)\n", strerror(errno));
+ WriteFdFmt(fd, "Error opening block device (%s)\n", strerror(errno));
return -1;
}
if (lseek64(data_device, 1024, SEEK_SET) < 0) {
- write_console(fd, "Error seeking to superblock\n");
+ WriteFdFmt(fd, "Error seeking to superblock\n");
adb_close(data_device);
return -1;
}
if (adb_read(data_device, &sb, sizeof(sb)) != sizeof(sb)) {
- write_console(fd, "Error reading superblock\n");
+ WriteFdFmt(fd, "Error reading superblock\n");
adb_close(data_device);
return -1;
}
@@ -95,74 +86,65 @@
int device = -1;
int retval = -1;
- if (make_block_device_writable(block_device)) {
- write_console(fd, "Could not make block device %s writable (%s).\n",
- block_device, strerror(errno));
+ if (!make_block_device_writable(block_device)) {
+ WriteFdFmt(fd, "Could not make block device %s writable (%s).\n",
+ block_device, strerror(errno));
goto errout;
}
device = adb_open(block_device, O_RDWR | O_CLOEXEC);
if (device == -1) {
- write_console(fd, "Could not open block device %s (%s).\n",
- block_device, strerror(errno));
- write_console(fd, "Maybe run adb remount?\n");
+ WriteFdFmt(fd, "Could not open block device %s (%s).\n", block_device, strerror(errno));
+ WriteFdFmt(fd, "Maybe run adb remount?\n");
goto errout;
}
// find the start of the verity metadata
if (get_target_device_size(fd, (char*)block_device, &device_length) < 0) {
- write_console(fd, "Could not get target device size.\n");
+ WriteFdFmt(fd, "Could not get target device size.\n");
goto errout;
}
if (lseek64(device, device_length, SEEK_SET) < 0) {
- write_console(fd,
- "Could not seek to start of verity metadata block.\n");
+ WriteFdFmt(fd, "Could not seek to start of verity metadata block.\n");
goto errout;
}
// check the magic number
- if (adb_read(device, &magic_number, sizeof(magic_number))
- != sizeof(magic_number)) {
- write_console(fd, "Couldn't read magic number!\n");
+ if (adb_read(device, &magic_number, sizeof(magic_number)) != sizeof(magic_number)) {
+ WriteFdFmt(fd, "Couldn't read magic number!\n");
goto errout;
}
if (!enable && magic_number == VERITY_METADATA_MAGIC_DISABLE) {
- write_console(fd, "Verity already disabled on %s\n", mount_point);
+ WriteFdFmt(fd, "Verity already disabled on %s\n", mount_point);
goto errout;
}
if (enable && magic_number == VERITY_METADATA_MAGIC_NUMBER) {
- write_console(fd, "Verity already enabled on %s\n", mount_point);
+ WriteFdFmt(fd, "Verity already enabled on %s\n", mount_point);
goto errout;
}
if (magic_number != VERITY_METADATA_MAGIC_NUMBER
&& magic_number != VERITY_METADATA_MAGIC_DISABLE) {
- write_console(fd,
- "Couldn't find verity metadata at offset %" PRIu64 "!\n",
- device_length);
+ WriteFdFmt(fd, "Couldn't find verity metadata at offset %" PRIu64 "!\n", device_length);
goto errout;
}
if (lseek64(device, device_length, SEEK_SET) < 0) {
- write_console(fd,
- "Could not seek to start of verity metadata block.\n");
+ WriteFdFmt(fd, "Could not seek to start of verity metadata block.\n");
goto errout;
}
if (adb_write(device, &new_magic, sizeof(new_magic)) != sizeof(new_magic)) {
- write_console(
- fd, "Could not set verity %s flag on device %s with error %s\n",
- enable ? "enabled" : "disabled",
- block_device, strerror(errno));
+ WriteFdFmt(fd, "Could not set verity %s flag on device %s with error %s\n",
+ enable ? "enabled" : "disabled",
+ block_device, strerror(errno));
goto errout;
}
- write_console(fd, "Verity %s on %s\n",
- enable ? "enabled" : "disabled",
- mount_point);
+ WriteFdFmt(fd, "Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
retval = 0;
errout:
if (device != -1)
@@ -181,14 +163,13 @@
property_get("ro.secure", propbuf, "0");
if (strcmp(propbuf, "1")) {
- write_console(fd, "verity not enabled - ENG build\n");
+ WriteFdFmt(fd, "verity not enabled - ENG build\n");
goto errout;
}
property_get("ro.debuggable", propbuf, "0");
if (strcmp(propbuf, "1")) {
- write_console(
- fd, "verity cannot be disabled/enabled - USER build\n");
+ WriteFdFmt(fd, "verity cannot be disabled/enabled - USER build\n");
goto errout;
}
@@ -198,8 +179,7 @@
fstab = fs_mgr_read_fstab(fstab_filename);
if (!fstab) {
- write_console(fd, "Failed to open %s\nMaybe run adb root?\n",
- fstab_filename);
+ WriteFdFmt(fd, "Failed to open %s\nMaybe run adb root?\n", fstab_filename);
goto errout;
}
@@ -215,12 +195,11 @@
}
if (any_changed) {
- write_console(
- fd, "Now reboot your device for settings to take effect\n");
+ WriteFdFmt(fd, "Now reboot your device for settings to take effect\n");
}
} else {
- write_console(fd, "%s-verity only works for userdebug builds\n",
- enable ? "enable" : "disable");
+ WriteFdFmt(fd, "%s-verity only works for userdebug builds\n",
+ enable ? "enable" : "disable");
}
errout:
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 12bc8d8..62cba6d 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#define TRACE_TAG TRACE_SOCKETS
+
+#include "sysdeps.h"
+
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
@@ -21,37 +25,18 @@
#include <string.h>
#include <unistd.h>
-#include "sysdeps.h"
-
-#define TRACE_TAG TRACE_SOCKETS
-#include "adb.h"
-#include "adb_io.h"
#if !ADB_HOST
#include "cutils/properties.h"
#endif
+
+#include "adb.h"
+#include "adb_io.h"
#include "transport.h"
ADB_MUTEX_DEFINE( socket_list_lock );
static void local_socket_close_locked(asocket *s);
-int sendfailmsg(int fd, const char *reason)
-{
- char buf[9];
- int len;
- len = strlen(reason);
- if (len > 0xffff) {
- len = 0xffff;
- }
-
- snprintf(buf, sizeof buf, "FAIL%04x", len);
- if (!WriteFdExactly(fd, buf, 8)) {
- return -1;
- }
-
- return WriteFdExactly(fd, reason, len) ? 0 : -1;
-}
-
static unsigned local_socket_next_id = 1;
static asocket local_socket_list = {
@@ -485,10 +470,10 @@
/* a Remote socket is used to send/receive data to/from a given transport object
** it needs to be closed when the transport is forcibly destroyed by the user
*/
-typedef struct aremotesocket {
+struct aremotesocket {
asocket socket;
adisconnect disconnect;
-} aremotesocket;
+};
static int remote_socket_enqueue(asocket *s, apacket *p)
{
@@ -606,7 +591,7 @@
s->ready = local_socket_ready;
s->shutdown = NULL;
s->close = local_socket_close;
- adb_write(s->fd, "OKAY", 4);
+ SendOkay(s->fd);
s->ready(s);
}
@@ -618,11 +603,11 @@
s->ready = local_socket_ready;
s->shutdown = NULL;
s->close = local_socket_close;
- sendfailmsg(s->fd, "closed");
+ SendFail(s->fd, "closed");
s->close(s);
}
-unsigned unhex(unsigned char *s, int len)
+static unsigned unhex(unsigned char *s, int len)
{
unsigned n = 0, c;
@@ -652,6 +637,8 @@
return n;
}
+#if ADB_HOST
+
#define PREFIX(str) { str, sizeof(str) - 1 }
static const struct prefix_struct {
const char *str;
@@ -668,7 +655,7 @@
skipping over the 'serial' parameter in the ADB protocol,
where parameter string may be a host:port string containing
the protocol delimiter (colon). */
-char *skip_host_serial(char *service) {
+static char *skip_host_serial(char *service) {
char *first_colon, *serial_end;
int i;
@@ -696,13 +683,15 @@
return serial_end;
}
+#endif // ADB_HOST
+
static int smart_socket_enqueue(asocket *s, apacket *p)
{
unsigned len;
#if ADB_HOST
char *service = NULL;
char* serial = NULL;
- transport_type ttype = kTransportAny;
+ TransportType type = kTransportAny;
#endif
D("SS(%d): enqueue %d\n", s->id, p->len);
@@ -759,13 +748,13 @@
service = serial_end + 1;
}
} else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) {
- ttype = kTransportUsb;
+ type = kTransportUsb;
service += strlen("host-usb:");
} else if (!strncmp(service, "host-local:", strlen("host-local:"))) {
- ttype = kTransportLocal;
+ type = kTransportLocal;
service += strlen("host-local:");
} else if (!strncmp(service, "host:", strlen("host:"))) {
- ttype = kTransportAny;
+ type = kTransportAny;
service += strlen("host:");
} else {
service = NULL;
@@ -779,7 +768,7 @@
** the OKAY or FAIL message and all we have to do
** is clean up.
*/
- if(handle_host_request(service, ttype, serial, s->peer->fd, s) == 0) {
+ if(handle_host_request(service, type, serial, s->peer->fd, s) == 0) {
/* XXX fail message? */
D( "SS(%d): handled host service '%s'\n", s->id, service );
goto fail;
@@ -797,7 +786,7 @@
s2 = create_host_service_socket(service, serial);
if(s2 == 0) {
D( "SS(%d): couldn't create host service '%s'\n", s->id, service );
- sendfailmsg(s->peer->fd, "unknown host service");
+ SendFail(s->peer->fd, "unknown host service");
goto fail;
}
@@ -808,7 +797,7 @@
** connection, and close this smart socket now
** that its work is done.
*/
- adb_write(s->peer->fd, "OKAY", 4);
+ SendOkay(s->peer->fd);
s->peer->ready = local_socket_ready;
s->peer->shutdown = NULL;
@@ -825,12 +814,11 @@
}
#else /* !ADB_HOST */
if (s->transport == NULL) {
- const char* error_string = "unknown failure";
- s->transport = acquire_one_transport (CS_ANY,
- kTransportAny, NULL, &error_string);
+ std::string error_msg = "unknown failure";
+ s->transport = acquire_one_transport(CS_ANY, kTransportAny, NULL, &error_msg);
if (s->transport == NULL) {
- sendfailmsg(s->peer->fd, error_string);
+ SendFail(s->peer->fd, error_msg);
goto fail;
}
}
@@ -840,7 +828,7 @@
/* if there's no remote we fail the connection
** right here and terminate it
*/
- sendfailmsg(s->peer->fd, "device offline (x)");
+ SendFail(s->peer->fd, "device offline (x)");
goto fail;
}
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index c317e3a..0aa1570 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -54,10 +54,6 @@
#include "fdevent.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
#define OS_PATH_SEPARATOR '\\'
#define OS_PATH_SEPARATOR_STR "\\"
#define ENV_PATH_SEPARATOR_STR ";"
@@ -83,19 +79,13 @@
LeaveCriticalSection( lock );
}
-typedef struct { unsigned tid; } adb_thread_t;
-
typedef void* (*adb_thread_func_t)(void* arg);
typedef void (*win_thread_func_t)(void* arg);
-static __inline__ int adb_thread_create( adb_thread_t *thread, adb_thread_func_t func, void* arg)
-{
- thread->tid = _beginthread( (win_thread_func_t)func, 0, arg );
- if (thread->tid == (unsigned)-1L) {
- return -1;
- }
- return 0;
+static __inline__ bool adb_thread_create(adb_thread_func_t func, void* arg) {
+ uintptr_t tid = _beginthread((win_thread_func_t)func, 0, arg);
+ return (tid != static_cast<uintptr_t>(-1L));
}
static __inline__ unsigned long adb_thread_id()
@@ -150,10 +140,8 @@
#undef close
#define close ____xxx_close
-static __inline__ int unix_read(int fd, void* buf, size_t len)
-{
- return read(fd, buf, len);
-}
+extern int unix_read(int fd, void* buf, size_t len);
+
#undef read
#define read ___xxx_read
@@ -277,8 +265,6 @@
return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
}
-extern char* adb_strtok_r(char *str, const char *delim, char **saveptr);
-
#else /* !_WIN32 a.k.a. Unix */
#include "fdevent.h"
@@ -298,10 +284,6 @@
#include <string.h>
#include <unistd.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
#define OS_PATH_SEPARATOR '/'
#define OS_PATH_SEPARATOR_STR "/"
#define ENV_PATH_SEPARATOR_STR ":"
@@ -441,18 +423,16 @@
#define unix_write adb_write
#define unix_close adb_close
-typedef pthread_t adb_thread_t;
-
typedef void* (*adb_thread_func_t)( void* arg );
-static __inline__ int adb_thread_create( adb_thread_t *pthread, adb_thread_func_t start, void* arg )
-{
- pthread_attr_t attr;
+static __inline__ bool adb_thread_create(adb_thread_func_t start, void* arg) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- pthread_attr_init (&attr);
- pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
-
- return pthread_create( pthread, &attr, start, arg );
+ pthread_t thread;
+ errno = pthread_create(&thread, &attr, start, arg);
+ return (errno == 0);
}
static __inline__ int adb_socket_setbufsize( int fd, int bufsize )
@@ -527,23 +507,11 @@
return path[0] == '/';
}
-static __inline__ char* adb_strtok_r(char *str, const char *delim, char **saveptr)
-{
- return strtok_r(str, delim, saveptr);
-}
-
static __inline__ unsigned long adb_thread_id()
{
return (unsigned long)pthread_self();
}
-#undef strtok_r
-#define strtok_r ___xxx_strtok_r
-
#endif /* !_WIN32 */
-#ifdef __cplusplus
-}
-#endif
-
#endif /* _ADB_SYSDEPS_H */
diff --git a/adb/sysdeps_win32.c b/adb/sysdeps_win32.c
deleted file mode 100644
index f132b8c..0000000
--- a/adb/sysdeps_win32.c
+++ /dev/null
@@ -1,2232 +0,0 @@
-#include "sysdeps.h"
-#include <winsock2.h>
-#include <windows.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#define TRACE_TAG TRACE_SYSDEPS
-#include "adb.h"
-
-extern void fatal(const char *fmt, ...);
-
-#define assert(cond) do { if (!(cond)) fatal( "assertion failed '%s' on %s:%ld\n", #cond, __FILE__, __LINE__ ); } while (0)
-
-/**************************************************************************/
-/**************************************************************************/
-/***** *****/
-/***** replaces libs/cutils/load_file.c *****/
-/***** *****/
-/**************************************************************************/
-/**************************************************************************/
-
-void *load_file(const char *fn, unsigned *_sz)
-{
- HANDLE file;
- char *data;
- DWORD file_size;
-
- file = CreateFile( fn,
- GENERIC_READ,
- FILE_SHARE_READ,
- NULL,
- OPEN_EXISTING,
- 0,
- NULL );
-
- if (file == INVALID_HANDLE_VALUE)
- return NULL;
-
- file_size = GetFileSize( file, NULL );
- data = NULL;
-
- if (file_size > 0) {
- data = (char*) malloc( file_size + 1 );
- if (data == NULL) {
- D("load_file: could not allocate %ld bytes\n", file_size );
- file_size = 0;
- } else {
- DWORD out_bytes;
-
- if ( !ReadFile( file, data, file_size, &out_bytes, NULL ) ||
- out_bytes != file_size )
- {
- D("load_file: could not read %ld bytes from '%s'\n", file_size, fn);
- free(data);
- data = NULL;
- file_size = 0;
- }
- }
- }
- CloseHandle( file );
-
- *_sz = (unsigned) file_size;
- return data;
-}
-
-/**************************************************************************/
-/**************************************************************************/
-/***** *****/
-/***** common file descriptor handling *****/
-/***** *****/
-/**************************************************************************/
-/**************************************************************************/
-
-typedef const struct FHClassRec_* FHClass;
-
-typedef struct FHRec_* FH;
-
-typedef struct EventHookRec_* EventHook;
-
-typedef struct FHClassRec_
-{
- void (*_fh_init) ( FH f );
- int (*_fh_close)( FH f );
- int (*_fh_lseek)( FH f, int pos, int origin );
- int (*_fh_read) ( FH f, void* buf, int len );
- int (*_fh_write)( FH f, const void* buf, int len );
- void (*_fh_hook) ( FH f, int events, EventHook hook );
-
-} FHClassRec;
-
-/* used to emulate unix-domain socket pairs */
-typedef struct SocketPairRec_* SocketPair;
-
-typedef struct FHRec_
-{
- FHClass clazz;
- int used;
- int eof;
- union {
- HANDLE handle;
- SOCKET socket;
- SocketPair pair;
- } u;
-
- HANDLE event;
- int mask;
-
- char name[32];
-
-} FHRec;
-
-#define fh_handle u.handle
-#define fh_socket u.socket
-#define fh_pair u.pair
-
-#define WIN32_FH_BASE 100
-
-#define WIN32_MAX_FHS 128
-
-static adb_mutex_t _win32_lock;
-static FHRec _win32_fhs[ WIN32_MAX_FHS ];
-static int _win32_fh_count;
-
-static FH
-_fh_from_int( int fd )
-{
- FH f;
-
- fd -= WIN32_FH_BASE;
-
- if (fd < 0 || fd >= _win32_fh_count) {
- D( "_fh_from_int: invalid fd %d\n", fd + WIN32_FH_BASE );
- errno = EBADF;
- return NULL;
- }
-
- f = &_win32_fhs[fd];
-
- if (f->used == 0) {
- D( "_fh_from_int: invalid fd %d\n", fd + WIN32_FH_BASE );
- errno = EBADF;
- return NULL;
- }
-
- return f;
-}
-
-
-static int
-_fh_to_int( FH f )
-{
- if (f && f->used && f >= _win32_fhs && f < _win32_fhs + WIN32_MAX_FHS)
- return (int)(f - _win32_fhs) + WIN32_FH_BASE;
-
- return -1;
-}
-
-static FH
-_fh_alloc( FHClass clazz )
-{
- int nn;
- FH f = NULL;
-
- adb_mutex_lock( &_win32_lock );
-
- if (_win32_fh_count < WIN32_MAX_FHS) {
- f = &_win32_fhs[ _win32_fh_count++ ];
- goto Exit;
- }
-
- for (nn = 0; nn < WIN32_MAX_FHS; nn++) {
- if ( _win32_fhs[nn].clazz == NULL) {
- f = &_win32_fhs[nn];
- goto Exit;
- }
- }
- D( "_fh_alloc: no more free file descriptors\n" );
-Exit:
- if (f) {
- f->clazz = clazz;
- f->used = 1;
- f->eof = 0;
- clazz->_fh_init(f);
- }
- adb_mutex_unlock( &_win32_lock );
- return f;
-}
-
-
-static int
-_fh_close( FH f )
-{
- if ( f->used ) {
- f->clazz->_fh_close( f );
- f->used = 0;
- f->eof = 0;
- f->clazz = NULL;
- }
- return 0;
-}
-
-/* forward definitions */
-static const FHClassRec _fh_file_class;
-static const FHClassRec _fh_socket_class;
-
-/**************************************************************************/
-/**************************************************************************/
-/***** *****/
-/***** file-based descriptor handling *****/
-/***** *****/
-/**************************************************************************/
-/**************************************************************************/
-
-static void
-_fh_file_init( FH f )
-{
- f->fh_handle = INVALID_HANDLE_VALUE;
-}
-
-static int
-_fh_file_close( FH f )
-{
- CloseHandle( f->fh_handle );
- f->fh_handle = INVALID_HANDLE_VALUE;
- return 0;
-}
-
-static int
-_fh_file_read( FH f, void* buf, int len )
-{
- DWORD read_bytes;
-
- if ( !ReadFile( f->fh_handle, buf, (DWORD)len, &read_bytes, NULL ) ) {
- D( "adb_read: could not read %d bytes from %s\n", len, f->name );
- errno = EIO;
- return -1;
- } else if (read_bytes < (DWORD)len) {
- f->eof = 1;
- }
- return (int)read_bytes;
-}
-
-static int
-_fh_file_write( FH f, const void* buf, int len )
-{
- DWORD wrote_bytes;
-
- if ( !WriteFile( f->fh_handle, buf, (DWORD)len, &wrote_bytes, NULL ) ) {
- D( "adb_file_write: could not write %d bytes from %s\n", len, f->name );
- errno = EIO;
- return -1;
- } else if (wrote_bytes < (DWORD)len) {
- f->eof = 1;
- }
- return (int)wrote_bytes;
-}
-
-static int
-_fh_file_lseek( FH f, int pos, int origin )
-{
- DWORD method;
- DWORD result;
-
- switch (origin)
- {
- case SEEK_SET: method = FILE_BEGIN; break;
- case SEEK_CUR: method = FILE_CURRENT; break;
- case SEEK_END: method = FILE_END; break;
- default:
- errno = EINVAL;
- return -1;
- }
-
- result = SetFilePointer( f->fh_handle, pos, NULL, method );
- if (result == INVALID_SET_FILE_POINTER) {
- errno = EIO;
- return -1;
- } else {
- f->eof = 0;
- }
- return (int)result;
-}
-
-static void _fh_file_hook( FH f, int event, EventHook eventhook ); /* forward */
-
-static const FHClassRec _fh_file_class =
-{
- _fh_file_init,
- _fh_file_close,
- _fh_file_lseek,
- _fh_file_read,
- _fh_file_write,
- _fh_file_hook
-};
-
-/**************************************************************************/
-/**************************************************************************/
-/***** *****/
-/***** file-based descriptor handling *****/
-/***** *****/
-/**************************************************************************/
-/**************************************************************************/
-
-int adb_open(const char* path, int options)
-{
- FH f;
-
- DWORD desiredAccess = 0;
- DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
-
- switch (options) {
- case O_RDONLY:
- desiredAccess = GENERIC_READ;
- break;
- case O_WRONLY:
- desiredAccess = GENERIC_WRITE;
- break;
- case O_RDWR:
- desiredAccess = GENERIC_READ | GENERIC_WRITE;
- break;
- default:
- D("adb_open: invalid options (0x%0x)\n", options);
- errno = EINVAL;
- return -1;
- }
-
- f = _fh_alloc( &_fh_file_class );
- if ( !f ) {
- errno = ENOMEM;
- return -1;
- }
-
- f->fh_handle = CreateFile( path, desiredAccess, shareMode, NULL, OPEN_EXISTING,
- 0, NULL );
-
- if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
- _fh_close(f);
- D( "adb_open: could not open '%s':", path );
- switch (GetLastError()) {
- case ERROR_FILE_NOT_FOUND:
- D( "file not found\n" );
- errno = ENOENT;
- return -1;
-
- case ERROR_PATH_NOT_FOUND:
- D( "path not found\n" );
- errno = ENOTDIR;
- return -1;
-
- default:
- D( "unknown error\n" );
- errno = ENOENT;
- return -1;
- }
- }
-
- snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path );
- D( "adb_open: '%s' => fd %d\n", path, _fh_to_int(f) );
- return _fh_to_int(f);
-}
-
-/* ignore mode on Win32 */
-int adb_creat(const char* path, int mode)
-{
- FH f;
-
- f = _fh_alloc( &_fh_file_class );
- if ( !f ) {
- errno = ENOMEM;
- return -1;
- }
-
- f->fh_handle = CreateFile( path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
- NULL );
-
- if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
- _fh_close(f);
- D( "adb_creat: could not open '%s':", path );
- switch (GetLastError()) {
- case ERROR_FILE_NOT_FOUND:
- D( "file not found\n" );
- errno = ENOENT;
- return -1;
-
- case ERROR_PATH_NOT_FOUND:
- D( "path not found\n" );
- errno = ENOTDIR;
- return -1;
-
- default:
- D( "unknown error\n" );
- errno = ENOENT;
- return -1;
- }
- }
- snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path );
- D( "adb_creat: '%s' => fd %d\n", path, _fh_to_int(f) );
- return _fh_to_int(f);
-}
-
-
-int adb_read(int fd, void* buf, int len)
-{
- FH f = _fh_from_int(fd);
-
- if (f == NULL) {
- return -1;
- }
-
- return f->clazz->_fh_read( f, buf, len );
-}
-
-
-int adb_write(int fd, const void* buf, int len)
-{
- FH f = _fh_from_int(fd);
-
- if (f == NULL) {
- return -1;
- }
-
- return f->clazz->_fh_write(f, buf, len);
-}
-
-
-int adb_lseek(int fd, int pos, int where)
-{
- FH f = _fh_from_int(fd);
-
- if (!f) {
- return -1;
- }
-
- return f->clazz->_fh_lseek(f, pos, where);
-}
-
-
-int adb_shutdown(int fd)
-{
- FH f = _fh_from_int(fd);
-
- if (!f || f->clazz != &_fh_socket_class) {
- D("adb_shutdown: invalid fd %d\n", fd);
- return -1;
- }
-
- D( "adb_shutdown: %s\n", f->name);
- shutdown( f->fh_socket, SD_BOTH );
- return 0;
-}
-
-
-int adb_close(int fd)
-{
- FH f = _fh_from_int(fd);
-
- if (!f) {
- return -1;
- }
-
- D( "adb_close: %s\n", f->name);
- _fh_close(f);
- return 0;
-}
-
-/**************************************************************************/
-/**************************************************************************/
-/***** *****/
-/***** socket-based file descriptors *****/
-/***** *****/
-/**************************************************************************/
-/**************************************************************************/
-
-#undef setsockopt
-
-static void
-_socket_set_errno( void )
-{
- switch (WSAGetLastError()) {
- case 0: errno = 0; break;
- case WSAEWOULDBLOCK: errno = EAGAIN; break;
- case WSAEINTR: errno = EINTR; break;
- default:
- D( "_socket_set_errno: unhandled value %d\n", WSAGetLastError() );
- errno = EINVAL;
- }
-}
-
-static void
-_fh_socket_init( FH f )
-{
- f->fh_socket = INVALID_SOCKET;
- f->event = WSACreateEvent();
- f->mask = 0;
-}
-
-static int
-_fh_socket_close( FH f )
-{
- /* gently tell any peer that we're closing the socket */
- shutdown( f->fh_socket, SD_BOTH );
- closesocket( f->fh_socket );
- f->fh_socket = INVALID_SOCKET;
- CloseHandle( f->event );
- f->mask = 0;
- return 0;
-}
-
-static int
-_fh_socket_lseek( FH f, int pos, int origin )
-{
- errno = EPIPE;
- return -1;
-}
-
-static int
-_fh_socket_read( FH f, void* buf, int len )
-{
- int result = recv( f->fh_socket, buf, len, 0 );
- if (result == SOCKET_ERROR) {
- _socket_set_errno();
- result = -1;
- }
- return result;
-}
-
-static int
-_fh_socket_write( FH f, const void* buf, int len )
-{
- int result = send( f->fh_socket, buf, len, 0 );
- if (result == SOCKET_ERROR) {
- _socket_set_errno();
- result = -1;
- }
- return result;
-}
-
-static void _fh_socket_hook( FH f, int event, EventHook hook ); /* forward */
-
-static const FHClassRec _fh_socket_class =
-{
- _fh_socket_init,
- _fh_socket_close,
- _fh_socket_lseek,
- _fh_socket_read,
- _fh_socket_write,
- _fh_socket_hook
-};
-
-/**************************************************************************/
-/**************************************************************************/
-/***** *****/
-/***** replacement for libs/cutils/socket_xxxx.c *****/
-/***** *****/
-/**************************************************************************/
-/**************************************************************************/
-
-#include <winsock2.h>
-
-static int _winsock_init;
-
-static void
-_cleanup_winsock( void )
-{
- WSACleanup();
-}
-
-static void
-_init_winsock( void )
-{
- if (!_winsock_init) {
- WSADATA wsaData;
- int rc = WSAStartup( MAKEWORD(2,2), &wsaData);
- if (rc != 0) {
- fatal( "adb: could not initialize Winsock\n" );
- }
- atexit( _cleanup_winsock );
- _winsock_init = 1;
- }
-}
-
-int socket_loopback_client(int port, int type)
-{
- FH f = _fh_alloc( &_fh_socket_class );
- struct sockaddr_in addr;
- SOCKET s;
-
- if (!f)
- return -1;
-
- if (!_winsock_init)
- _init_winsock();
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-
- s = socket(AF_INET, type, 0);
- if(s == INVALID_SOCKET) {
- D("socket_loopback_client: could not create socket\n" );
- _fh_close(f);
- return -1;
- }
-
- f->fh_socket = s;
- if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- D("socket_loopback_client: could not connect to %s:%d\n", type != SOCK_STREAM ? "udp" : "tcp", port );
- _fh_close(f);
- return -1;
- }
- snprintf( f->name, sizeof(f->name), "%d(lo-client:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
- D( "socket_loopback_client: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
- return _fh_to_int(f);
-}
-
-#define LISTEN_BACKLOG 4
-
-int socket_loopback_server(int port, int type)
-{
- FH f = _fh_alloc( &_fh_socket_class );
- struct sockaddr_in addr;
- SOCKET s;
- int n;
-
- if (!f) {
- return -1;
- }
-
- if (!_winsock_init)
- _init_winsock();
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-
- s = socket(AF_INET, type, 0);
- if(s == INVALID_SOCKET) return -1;
-
- f->fh_socket = s;
-
- n = 1;
- setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n));
-
- if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- _fh_close(f);
- return -1;
- }
- if (type == SOCK_STREAM) {
- int ret;
-
- ret = listen(s, LISTEN_BACKLOG);
- if (ret < 0) {
- _fh_close(f);
- return -1;
- }
- }
- snprintf( f->name, sizeof(f->name), "%d(lo-server:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
- D( "socket_loopback_server: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
- return _fh_to_int(f);
-}
-
-
-int socket_network_client(const char *host, int port, int type)
-{
- FH f = _fh_alloc( &_fh_socket_class );
- struct hostent *hp;
- struct sockaddr_in addr;
- SOCKET s;
-
- if (!f)
- return -1;
-
- if (!_winsock_init)
- _init_winsock();
-
- hp = gethostbyname(host);
- if(hp == 0) {
- _fh_close(f);
- return -1;
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = hp->h_addrtype;
- addr.sin_port = htons(port);
- memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
-
- s = socket(hp->h_addrtype, type, 0);
- if(s == INVALID_SOCKET) {
- _fh_close(f);
- return -1;
- }
- f->fh_socket = s;
-
- if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- _fh_close(f);
- return -1;
- }
-
- snprintf( f->name, sizeof(f->name), "%d(net-client:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
- D( "socket_network_client: host '%s' port %d type %s => fd %d\n", host, port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
- return _fh_to_int(f);
-}
-
-
-int socket_network_client_timeout(const char *host, int port, int type, int timeout)
-{
- // TODO: implement timeouts for Windows.
- return socket_network_client(host, port, type);
-}
-
-
-int socket_inaddr_any_server(int port, int type)
-{
- FH f = _fh_alloc( &_fh_socket_class );
- struct sockaddr_in addr;
- SOCKET s;
- int n;
-
- if (!f)
- return -1;
-
- if (!_winsock_init)
- _init_winsock();
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = htonl(INADDR_ANY);
-
- s = socket(AF_INET, type, 0);
- if(s == INVALID_SOCKET) {
- _fh_close(f);
- return -1;
- }
-
- f->fh_socket = s;
- n = 1;
- setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n));
-
- if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- _fh_close(f);
- return -1;
- }
-
- if (type == SOCK_STREAM) {
- int ret;
-
- ret = listen(s, LISTEN_BACKLOG);
- if (ret < 0) {
- _fh_close(f);
- return -1;
- }
- }
- snprintf( f->name, sizeof(f->name), "%d(any-server:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
- D( "socket_inaddr_server: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
- return _fh_to_int(f);
-}
-
-#undef accept
-int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen)
-{
- FH serverfh = _fh_from_int(serverfd);
- FH fh;
-
- if ( !serverfh || serverfh->clazz != &_fh_socket_class ) {
- D( "adb_socket_accept: invalid fd %d\n", serverfd );
- return -1;
- }
-
- fh = _fh_alloc( &_fh_socket_class );
- if (!fh) {
- D( "adb_socket_accept: not enough memory to allocate accepted socket descriptor\n" );
- return -1;
- }
-
- fh->fh_socket = accept( serverfh->fh_socket, addr, addrlen );
- if (fh->fh_socket == INVALID_SOCKET) {
- _fh_close( fh );
- D( "adb_socket_accept: accept on fd %d return error %ld\n", serverfd, GetLastError() );
- return -1;
- }
-
- snprintf( fh->name, sizeof(fh->name), "%d(accept:%s)", _fh_to_int(fh), serverfh->name );
- D( "adb_socket_accept on fd %d returns fd %d\n", serverfd, _fh_to_int(fh) );
- return _fh_to_int(fh);
-}
-
-
-int adb_setsockopt( int fd, int level, int optname, const void* optval, socklen_t optlen )
-{
- FH fh = _fh_from_int(fd);
-
- if ( !fh || fh->clazz != &_fh_socket_class ) {
- D("adb_setsockopt: invalid fd %d\n", fd);
- return -1;
- }
-
- return setsockopt( fh->fh_socket, level, optname, optval, optlen );
-}
-
-/**************************************************************************/
-/**************************************************************************/
-/***** *****/
-/***** emulated socketpairs *****/
-/***** *****/
-/**************************************************************************/
-/**************************************************************************/
-
-/* we implement socketpairs directly in use space for the following reasons:
- * - it avoids copying data from/to the Nt kernel
- * - it allows us to implement fdevent hooks easily and cheaply, something
- * that is not possible with standard Win32 pipes !!
- *
- * basically, we use two circular buffers, each one corresponding to a given
- * direction.
- *
- * each buffer is implemented as two regions:
- *
- * region A which is (a_start,a_end)
- * region B which is (0, b_end) with b_end <= a_start
- *
- * an empty buffer has: a_start = a_end = b_end = 0
- *
- * a_start is the pointer where we start reading data
- * a_end is the pointer where we start writing data, unless it is BUFFER_SIZE,
- * then you start writing at b_end
- *
- * the buffer is full when b_end == a_start && a_end == BUFFER_SIZE
- *
- * there is room when b_end < a_start || a_end < BUFER_SIZE
- *
- * when reading, a_start is incremented, it a_start meets a_end, then
- * we do: a_start = 0, a_end = b_end, b_end = 0, and keep going on..
- */
-
-#define BIP_BUFFER_SIZE 4096
-
-#if 0
-#include <stdio.h>
-# define BIPD(x) D x
-# define BIPDUMP bip_dump_hex
-
-static void bip_dump_hex( const unsigned char* ptr, size_t len )
-{
- int nn, len2 = len;
-
- if (len2 > 8) len2 = 8;
-
- for (nn = 0; nn < len2; nn++)
- printf("%02x", ptr[nn]);
- printf(" ");
-
- for (nn = 0; nn < len2; nn++) {
- int c = ptr[nn];
- if (c < 32 || c > 127)
- c = '.';
- printf("%c", c);
- }
- printf("\n");
- fflush(stdout);
-}
-
-#else
-# define BIPD(x) do {} while (0)
-# define BIPDUMP(p,l) BIPD(p)
-#endif
-
-typedef struct BipBufferRec_
-{
- int a_start;
- int a_end;
- int b_end;
- int fdin;
- int fdout;
- int closed;
- int can_write; /* boolean */
- HANDLE evt_write; /* event signaled when one can write to a buffer */
- int can_read; /* boolean */
- HANDLE evt_read; /* event signaled when one can read from a buffer */
- CRITICAL_SECTION lock;
- unsigned char buff[ BIP_BUFFER_SIZE ];
-
-} BipBufferRec, *BipBuffer;
-
-static void
-bip_buffer_init( BipBuffer buffer )
-{
- D( "bit_buffer_init %p\n", buffer );
- buffer->a_start = 0;
- buffer->a_end = 0;
- buffer->b_end = 0;
- buffer->can_write = 1;
- buffer->can_read = 0;
- buffer->fdin = 0;
- buffer->fdout = 0;
- buffer->closed = 0;
- buffer->evt_write = CreateEvent( NULL, TRUE, TRUE, NULL );
- buffer->evt_read = CreateEvent( NULL, TRUE, FALSE, NULL );
- InitializeCriticalSection( &buffer->lock );
-}
-
-static void
-bip_buffer_close( BipBuffer bip )
-{
- bip->closed = 1;
-
- if (!bip->can_read) {
- SetEvent( bip->evt_read );
- }
- if (!bip->can_write) {
- SetEvent( bip->evt_write );
- }
-}
-
-static void
-bip_buffer_done( BipBuffer bip )
-{
- BIPD(( "bip_buffer_done: %d->%d\n", bip->fdin, bip->fdout ));
- CloseHandle( bip->evt_read );
- CloseHandle( bip->evt_write );
- DeleteCriticalSection( &bip->lock );
-}
-
-static int
-bip_buffer_write( BipBuffer bip, const void* src, int len )
-{
- int avail, count = 0;
-
- if (len <= 0)
- return 0;
-
- BIPD(( "bip_buffer_write: enter %d->%d len %d\n", bip->fdin, bip->fdout, len ));
- BIPDUMP( src, len );
-
- EnterCriticalSection( &bip->lock );
-
- while (!bip->can_write) {
- int ret;
- LeaveCriticalSection( &bip->lock );
-
- if (bip->closed) {
- errno = EPIPE;
- return -1;
- }
- /* spinlocking here is probably unfair, but let's live with it */
- ret = WaitForSingleObject( bip->evt_write, INFINITE );
- if (ret != WAIT_OBJECT_0) { /* buffer probably closed */
- D( "bip_buffer_write: error %d->%d WaitForSingleObject returned %d, error %ld\n", bip->fdin, bip->fdout, ret, GetLastError() );
- return 0;
- }
- if (bip->closed) {
- errno = EPIPE;
- return -1;
- }
- EnterCriticalSection( &bip->lock );
- }
-
- BIPD(( "bip_buffer_write: exec %d->%d len %d\n", bip->fdin, bip->fdout, len ));
-
- avail = BIP_BUFFER_SIZE - bip->a_end;
- if (avail > 0)
- {
- /* we can append to region A */
- if (avail > len)
- avail = len;
-
- memcpy( bip->buff + bip->a_end, src, avail );
- src = (const char *)src + avail;
- count += avail;
- len -= avail;
-
- bip->a_end += avail;
- if (bip->a_end == BIP_BUFFER_SIZE && bip->a_start == 0) {
- bip->can_write = 0;
- ResetEvent( bip->evt_write );
- goto Exit;
- }
- }
-
- if (len == 0)
- goto Exit;
-
- avail = bip->a_start - bip->b_end;
- assert( avail > 0 ); /* since can_write is TRUE */
-
- if (avail > len)
- avail = len;
-
- memcpy( bip->buff + bip->b_end, src, avail );
- count += avail;
- bip->b_end += avail;
-
- if (bip->b_end == bip->a_start) {
- bip->can_write = 0;
- ResetEvent( bip->evt_write );
- }
-
-Exit:
- assert( count > 0 );
-
- if ( !bip->can_read ) {
- bip->can_read = 1;
- SetEvent( bip->evt_read );
- }
-
- BIPD(( "bip_buffer_write: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n",
- bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read ));
- LeaveCriticalSection( &bip->lock );
-
- return count;
- }
-
-static int
-bip_buffer_read( BipBuffer bip, void* dst, int len )
-{
- int avail, count = 0;
-
- if (len <= 0)
- return 0;
-
- BIPD(( "bip_buffer_read: enter %d->%d len %d\n", bip->fdin, bip->fdout, len ));
-
- EnterCriticalSection( &bip->lock );
- while ( !bip->can_read )
- {
-#if 0
- LeaveCriticalSection( &bip->lock );
- errno = EAGAIN;
- return -1;
-#else
- int ret;
- LeaveCriticalSection( &bip->lock );
-
- if (bip->closed) {
- errno = EPIPE;
- return -1;
- }
-
- ret = WaitForSingleObject( bip->evt_read, INFINITE );
- if (ret != WAIT_OBJECT_0) { /* probably closed buffer */
- D( "bip_buffer_read: error %d->%d WaitForSingleObject returned %d, error %ld\n", bip->fdin, bip->fdout, ret, GetLastError());
- return 0;
- }
- if (bip->closed) {
- errno = EPIPE;
- return -1;
- }
- EnterCriticalSection( &bip->lock );
-#endif
- }
-
- BIPD(( "bip_buffer_read: exec %d->%d len %d\n", bip->fdin, bip->fdout, len ));
-
- avail = bip->a_end - bip->a_start;
- assert( avail > 0 ); /* since can_read is TRUE */
-
- if (avail > len)
- avail = len;
-
- memcpy( dst, bip->buff + bip->a_start, avail );
- dst = (char *)dst + avail;
- count += avail;
- len -= avail;
-
- bip->a_start += avail;
- if (bip->a_start < bip->a_end)
- goto Exit;
-
- bip->a_start = 0;
- bip->a_end = bip->b_end;
- bip->b_end = 0;
-
- avail = bip->a_end;
- if (avail > 0) {
- if (avail > len)
- avail = len;
- memcpy( dst, bip->buff, avail );
- count += avail;
- bip->a_start += avail;
-
- if ( bip->a_start < bip->a_end )
- goto Exit;
-
- bip->a_start = bip->a_end = 0;
- }
-
- bip->can_read = 0;
- ResetEvent( bip->evt_read );
-
-Exit:
- assert( count > 0 );
-
- if (!bip->can_write ) {
- bip->can_write = 1;
- SetEvent( bip->evt_write );
- }
-
- BIPDUMP( (const unsigned char*)dst - count, count );
- BIPD(( "bip_buffer_read: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n",
- bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read ));
- LeaveCriticalSection( &bip->lock );
-
- return count;
-}
-
-typedef struct SocketPairRec_
-{
- BipBufferRec a2b_bip;
- BipBufferRec b2a_bip;
- FH a_fd;
- int used;
-
-} SocketPairRec;
-
-void _fh_socketpair_init( FH f )
-{
- f->fh_pair = NULL;
-}
-
-static int
-_fh_socketpair_close( FH f )
-{
- if ( f->fh_pair ) {
- SocketPair pair = f->fh_pair;
-
- if ( f == pair->a_fd ) {
- pair->a_fd = NULL;
- }
-
- bip_buffer_close( &pair->b2a_bip );
- bip_buffer_close( &pair->a2b_bip );
-
- if ( --pair->used == 0 ) {
- bip_buffer_done( &pair->b2a_bip );
- bip_buffer_done( &pair->a2b_bip );
- free( pair );
- }
- f->fh_pair = NULL;
- }
- return 0;
-}
-
-static int
-_fh_socketpair_lseek( FH f, int pos, int origin )
-{
- errno = ESPIPE;
- return -1;
-}
-
-static int
-_fh_socketpair_read( FH f, void* buf, int len )
-{
- SocketPair pair = f->fh_pair;
- BipBuffer bip;
-
- if (!pair)
- return -1;
-
- if ( f == pair->a_fd )
- bip = &pair->b2a_bip;
- else
- bip = &pair->a2b_bip;
-
- return bip_buffer_read( bip, buf, len );
-}
-
-static int
-_fh_socketpair_write( FH f, const void* buf, int len )
-{
- SocketPair pair = f->fh_pair;
- BipBuffer bip;
-
- if (!pair)
- return -1;
-
- if ( f == pair->a_fd )
- bip = &pair->a2b_bip;
- else
- bip = &pair->b2a_bip;
-
- return bip_buffer_write( bip, buf, len );
-}
-
-
-static void _fh_socketpair_hook( FH f, int event, EventHook hook ); /* forward */
-
-static const FHClassRec _fh_socketpair_class =
-{
- _fh_socketpair_init,
- _fh_socketpair_close,
- _fh_socketpair_lseek,
- _fh_socketpair_read,
- _fh_socketpair_write,
- _fh_socketpair_hook
-};
-
-
-int adb_socketpair( int sv[2] )
-{
- FH fa, fb;
- SocketPair pair;
-
- fa = _fh_alloc( &_fh_socketpair_class );
- fb = _fh_alloc( &_fh_socketpair_class );
-
- if (!fa || !fb)
- goto Fail;
-
- pair = malloc( sizeof(*pair) );
- if (pair == NULL) {
- D("adb_socketpair: not enough memory to allocate pipes\n" );
- goto Fail;
- }
-
- bip_buffer_init( &pair->a2b_bip );
- bip_buffer_init( &pair->b2a_bip );
-
- fa->fh_pair = pair;
- fb->fh_pair = pair;
- pair->used = 2;
- pair->a_fd = fa;
-
- sv[0] = _fh_to_int(fa);
- sv[1] = _fh_to_int(fb);
-
- pair->a2b_bip.fdin = sv[0];
- pair->a2b_bip.fdout = sv[1];
- pair->b2a_bip.fdin = sv[1];
- pair->b2a_bip.fdout = sv[0];
-
- snprintf( fa->name, sizeof(fa->name), "%d(pair:%d)", sv[0], sv[1] );
- snprintf( fb->name, sizeof(fb->name), "%d(pair:%d)", sv[1], sv[0] );
- D( "adb_socketpair: returns (%d, %d)\n", sv[0], sv[1] );
- return 0;
-
-Fail:
- _fh_close(fb);
- _fh_close(fa);
- return -1;
-}
-
-/**************************************************************************/
-/**************************************************************************/
-/***** *****/
-/***** fdevents emulation *****/
-/***** *****/
-/***** this is a very simple implementation, we rely on the fact *****/
-/***** that ADB doesn't use FDE_ERROR. *****/
-/***** *****/
-/**************************************************************************/
-/**************************************************************************/
-
-#define FATAL(x...) fatal(__FUNCTION__, x)
-
-#if DEBUG
-static void dump_fde(fdevent *fde, const char *info)
-{
- fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
- fde->state & FDE_READ ? 'R' : ' ',
- fde->state & FDE_WRITE ? 'W' : ' ',
- fde->state & FDE_ERROR ? 'E' : ' ',
- info);
-}
-#else
-#define dump_fde(fde, info) do { } while(0)
-#endif
-
-#define FDE_EVENTMASK 0x00ff
-#define FDE_STATEMASK 0xff00
-
-#define FDE_ACTIVE 0x0100
-#define FDE_PENDING 0x0200
-#define FDE_CREATED 0x0400
-
-static void fdevent_plist_enqueue(fdevent *node);
-static void fdevent_plist_remove(fdevent *node);
-static fdevent *fdevent_plist_dequeue(void);
-
-static fdevent list_pending = {
- .next = &list_pending,
- .prev = &list_pending,
-};
-
-static fdevent **fd_table = 0;
-static int fd_table_max = 0;
-
-typedef struct EventLooperRec_* EventLooper;
-
-typedef struct EventHookRec_
-{
- EventHook next;
- FH fh;
- HANDLE h;
- int wanted; /* wanted event flags */
- int ready; /* ready event flags */
- void* aux;
- void (*prepare)( EventHook hook );
- int (*start) ( EventHook hook );
- void (*stop) ( EventHook hook );
- int (*check) ( EventHook hook );
- int (*peek) ( EventHook hook );
-} EventHookRec;
-
-static EventHook _free_hooks;
-
-static EventHook
-event_hook_alloc( FH fh )
-{
- EventHook hook = _free_hooks;
- if (hook != NULL)
- _free_hooks = hook->next;
- else {
- hook = malloc( sizeof(*hook) );
- if (hook == NULL)
- fatal( "could not allocate event hook\n" );
- }
- hook->next = NULL;
- hook->fh = fh;
- hook->wanted = 0;
- hook->ready = 0;
- hook->h = INVALID_HANDLE_VALUE;
- hook->aux = NULL;
-
- hook->prepare = NULL;
- hook->start = NULL;
- hook->stop = NULL;
- hook->check = NULL;
- hook->peek = NULL;
-
- return hook;
-}
-
-static void
-event_hook_free( EventHook hook )
-{
- hook->fh = NULL;
- hook->wanted = 0;
- hook->ready = 0;
- hook->next = _free_hooks;
- _free_hooks = hook;
-}
-
-
-static void
-event_hook_signal( EventHook hook )
-{
- FH f = hook->fh;
- int fd = _fh_to_int(f);
- fdevent* fde = fd_table[ fd - WIN32_FH_BASE ];
-
- if (fde != NULL && fde->fd == fd) {
- if ((fde->state & FDE_PENDING) == 0) {
- fde->state |= FDE_PENDING;
- fdevent_plist_enqueue( fde );
- }
- fde->events |= hook->wanted;
- }
-}
-
-
-#define MAX_LOOPER_HANDLES WIN32_MAX_FHS
-
-typedef struct EventLooperRec_
-{
- EventHook hooks;
- HANDLE htab[ MAX_LOOPER_HANDLES ];
- int htab_count;
-
-} EventLooperRec;
-
-static EventHook*
-event_looper_find_p( EventLooper looper, FH fh )
-{
- EventHook *pnode = &looper->hooks;
- EventHook node = *pnode;
- for (;;) {
- if ( node == NULL || node->fh == fh )
- break;
- pnode = &node->next;
- node = *pnode;
- }
- return pnode;
-}
-
-static void
-event_looper_hook( EventLooper looper, int fd, int events )
-{
- FH f = _fh_from_int(fd);
- EventHook *pnode;
- EventHook node;
-
- if (f == NULL) /* invalid arg */ {
- D("event_looper_hook: invalid fd=%d\n", fd);
- return;
- }
-
- pnode = event_looper_find_p( looper, f );
- node = *pnode;
- if ( node == NULL ) {
- node = event_hook_alloc( f );
- node->next = *pnode;
- *pnode = node;
- }
-
- if ( (node->wanted & events) != events ) {
- /* this should update start/stop/check/peek */
- D("event_looper_hook: call hook for %d (new=%x, old=%x)\n",
- fd, node->wanted, events);
- f->clazz->_fh_hook( f, events & ~node->wanted, node );
- node->wanted |= events;
- } else {
- D("event_looper_hook: ignoring events %x for %d wanted=%x)\n",
- events, fd, node->wanted);
- }
-}
-
-static void
-event_looper_unhook( EventLooper looper, int fd, int events )
-{
- FH fh = _fh_from_int(fd);
- EventHook *pnode = event_looper_find_p( looper, fh );
- EventHook node = *pnode;
-
- if (node != NULL) {
- int events2 = events & node->wanted;
- if ( events2 == 0 ) {
- D( "event_looper_unhook: events %x not registered for fd %d\n", events, fd );
- return;
- }
- node->wanted &= ~events2;
- if (!node->wanted) {
- *pnode = node->next;
- event_hook_free( node );
- }
- }
-}
-
-/*
- * A fixer for WaitForMultipleObjects on condition that there are more than 64
- * handles to wait on.
- *
- * In cetain cases DDMS may establish more than 64 connections with ADB. For
- * instance, this may happen if there are more than 64 processes running on a
- * device, or there are multiple devices connected (including the emulator) with
- * the combined number of running processes greater than 64. In this case using
- * WaitForMultipleObjects to wait on connection events simply wouldn't cut,
- * because of the API limitations (64 handles max). So, we need to provide a way
- * to scale WaitForMultipleObjects to accept an arbitrary number of handles. The
- * easiest (and "Microsoft recommended") way to do that would be dividing the
- * handle array into chunks with the chunk size less than 64, and fire up as many
- * waiting threads as there are chunks. Then each thread would wait on a chunk of
- * handles, and will report back to the caller which handle has been set.
- * Here is the implementation of that algorithm.
- */
-
-/* Number of handles to wait on in each wating thread. */
-#define WAIT_ALL_CHUNK_SIZE 63
-
-/* Descriptor for a wating thread */
-typedef struct WaitForAllParam {
- /* A handle to an event to signal when waiting is over. This handle is shared
- * accross all the waiting threads, so each waiting thread knows when any
- * other thread has exited, so it can exit too. */
- HANDLE main_event;
- /* Upon exit from a waiting thread contains the index of the handle that has
- * been signaled. The index is an absolute index of the signaled handle in
- * the original array. This pointer is shared accross all the waiting threads
- * and it's not guaranteed (due to a race condition) that when all the
- * waiting threads exit, the value contained here would indicate the first
- * handle that was signaled. This is fine, because the caller cares only
- * about any handle being signaled. It doesn't care about the order, nor
- * about the whole list of handles that were signaled. */
- LONG volatile *signaled_index;
- /* Array of handles to wait on in a waiting thread. */
- HANDLE* handles;
- /* Number of handles in 'handles' array to wait on. */
- int handles_count;
- /* Index inside the main array of the first handle in the 'handles' array. */
- int first_handle_index;
- /* Waiting thread handle. */
- HANDLE thread;
-} WaitForAllParam;
-
-/* Waiting thread routine. */
-static unsigned __stdcall
-_in_waiter_thread(void* arg)
-{
- HANDLE wait_on[WAIT_ALL_CHUNK_SIZE + 1];
- int res;
- WaitForAllParam* const param = (WaitForAllParam*)arg;
-
- /* We have to wait on the main_event in order to be notified when any of the
- * sibling threads is exiting. */
- wait_on[0] = param->main_event;
- /* The rest of the handles go behind the main event handle. */
- memcpy(wait_on + 1, param->handles, param->handles_count * sizeof(HANDLE));
-
- res = WaitForMultipleObjects(param->handles_count + 1, wait_on, FALSE, INFINITE);
- if (res > 0 && res < (param->handles_count + 1)) {
- /* One of the original handles got signaled. Save its absolute index into
- * the output variable. */
- InterlockedCompareExchange(param->signaled_index,
- res - 1L + param->first_handle_index, -1L);
- }
-
- /* Notify the caller (and the siblings) that the wait is over. */
- SetEvent(param->main_event);
-
- _endthreadex(0);
- return 0;
-}
-
-/* WaitForMultipeObjects fixer routine.
- * Param:
- * handles Array of handles to wait on.
- * handles_count Number of handles in the array.
- * Return:
- * (>= 0 && < handles_count) - Index of the signaled handle in the array, or
- * WAIT_FAILED on an error.
- */
-static int
-_wait_for_all(HANDLE* handles, int handles_count)
-{
- WaitForAllParam* threads;
- HANDLE main_event;
- int chunks, chunk, remains;
-
- /* This variable is going to be accessed by several threads at the same time,
- * this is bound to fail randomly when the core is run on multi-core machines.
- * To solve this, we need to do the following (1 _and_ 2):
- * 1. Use the "volatile" qualifier to ensure the compiler doesn't optimize
- * out the reads/writes in this function unexpectedly.
- * 2. Ensure correct memory ordering. The "simple" way to do that is to wrap
- * all accesses inside a critical section. But we can also use
- * InterlockedCompareExchange() which always provide a full memory barrier
- * on Win32.
- */
- volatile LONG sig_index = -1;
-
- /* Calculate number of chunks, and allocate thread param array. */
- chunks = handles_count / WAIT_ALL_CHUNK_SIZE;
- remains = handles_count % WAIT_ALL_CHUNK_SIZE;
- threads = (WaitForAllParam*)malloc((chunks + (remains ? 1 : 0)) *
- sizeof(WaitForAllParam));
- if (threads == NULL) {
- D("Unable to allocate thread array for %d handles.", handles_count);
- return (int)WAIT_FAILED;
- }
-
- /* Create main event to wait on for all waiting threads. This is a "manualy
- * reset" event that will remain set once it was set. */
- main_event = CreateEvent(NULL, TRUE, FALSE, NULL);
- if (main_event == NULL) {
- D("Unable to create main event. Error: %d", (int)GetLastError());
- free(threads);
- return (int)WAIT_FAILED;
- }
-
- /*
- * Initialize waiting thread parameters.
- */
-
- for (chunk = 0; chunk < chunks; chunk++) {
- threads[chunk].main_event = main_event;
- threads[chunk].signaled_index = &sig_index;
- threads[chunk].first_handle_index = WAIT_ALL_CHUNK_SIZE * chunk;
- threads[chunk].handles = handles + threads[chunk].first_handle_index;
- threads[chunk].handles_count = WAIT_ALL_CHUNK_SIZE;
- }
- if (remains) {
- threads[chunk].main_event = main_event;
- threads[chunk].signaled_index = &sig_index;
- threads[chunk].first_handle_index = WAIT_ALL_CHUNK_SIZE * chunk;
- threads[chunk].handles = handles + threads[chunk].first_handle_index;
- threads[chunk].handles_count = remains;
- chunks++;
- }
-
- /* Start the waiting threads. */
- for (chunk = 0; chunk < chunks; chunk++) {
- /* Note that using adb_thread_create is not appropriate here, since we
- * need a handle to wait on for thread termination. */
- threads[chunk].thread = (HANDLE)_beginthreadex(NULL, 0, _in_waiter_thread,
- &threads[chunk], 0, NULL);
- if (threads[chunk].thread == NULL) {
- /* Unable to create a waiter thread. Collapse. */
- D("Unable to create a waiting thread %d of %d. errno=%d",
- chunk, chunks, errno);
- chunks = chunk;
- SetEvent(main_event);
- break;
- }
- }
-
- /* Wait on any of the threads to get signaled. */
- WaitForSingleObject(main_event, INFINITE);
-
- /* Wait on all the waiting threads to exit. */
- for (chunk = 0; chunk < chunks; chunk++) {
- WaitForSingleObject(threads[chunk].thread, INFINITE);
- CloseHandle(threads[chunk].thread);
- }
-
- CloseHandle(main_event);
- free(threads);
-
-
- const int ret = (int)InterlockedCompareExchange(&sig_index, -1, -1);
- return (ret >= 0) ? ret : (int)WAIT_FAILED;
-}
-
-static EventLooperRec win32_looper;
-
-static void fdevent_init(void)
-{
- win32_looper.htab_count = 0;
- win32_looper.hooks = NULL;
-}
-
-static void fdevent_connect(fdevent *fde)
-{
- EventLooper looper = &win32_looper;
- int events = fde->state & FDE_EVENTMASK;
-
- if (events != 0)
- event_looper_hook( looper, fde->fd, events );
-}
-
-static void fdevent_disconnect(fdevent *fde)
-{
- EventLooper looper = &win32_looper;
- int events = fde->state & FDE_EVENTMASK;
-
- if (events != 0)
- event_looper_unhook( looper, fde->fd, events );
-}
-
-static void fdevent_update(fdevent *fde, unsigned events)
-{
- EventLooper looper = &win32_looper;
- unsigned events0 = fde->state & FDE_EVENTMASK;
-
- if (events != events0) {
- int removes = events0 & ~events;
- int adds = events & ~events0;
- if (removes) {
- D("fdevent_update: remove %x from %d\n", removes, fde->fd);
- event_looper_unhook( looper, fde->fd, removes );
- }
- if (adds) {
- D("fdevent_update: add %x to %d\n", adds, fde->fd);
- event_looper_hook ( looper, fde->fd, adds );
- }
- }
-}
-
-static void fdevent_process()
-{
- EventLooper looper = &win32_looper;
- EventHook hook;
- int gotone = 0;
-
- /* if we have at least one ready hook, execute it/them */
- for (hook = looper->hooks; hook; hook = hook->next) {
- hook->ready = 0;
- if (hook->prepare) {
- hook->prepare(hook);
- if (hook->ready != 0) {
- event_hook_signal( hook );
- gotone = 1;
- }
- }
- }
-
- /* nothing's ready yet, so wait for something to happen */
- if (!gotone)
- {
- looper->htab_count = 0;
-
- for (hook = looper->hooks; hook; hook = hook->next)
- {
- if (hook->start && !hook->start(hook)) {
- D( "fdevent_process: error when starting a hook\n" );
- return;
- }
- if (hook->h != INVALID_HANDLE_VALUE) {
- int nn;
-
- for (nn = 0; nn < looper->htab_count; nn++)
- {
- if ( looper->htab[nn] == hook->h )
- goto DontAdd;
- }
- looper->htab[ looper->htab_count++ ] = hook->h;
- DontAdd:
- ;
- }
- }
-
- if (looper->htab_count == 0) {
- D( "fdevent_process: nothing to wait for !!\n" );
- return;
- }
-
- do
- {
- int wait_ret;
-
- D( "adb_win32: waiting for %d events\n", looper->htab_count );
- if (looper->htab_count > MAXIMUM_WAIT_OBJECTS) {
- D("handle count %d exceeds MAXIMUM_WAIT_OBJECTS.\n", looper->htab_count);
- wait_ret = _wait_for_all(looper->htab, looper->htab_count);
- } else {
- wait_ret = WaitForMultipleObjects( looper->htab_count, looper->htab, FALSE, INFINITE );
- }
- if (wait_ret == (int)WAIT_FAILED) {
- D( "adb_win32: wait failed, error %ld\n", GetLastError() );
- } else {
- D( "adb_win32: got one (index %d)\n", wait_ret );
-
- /* according to Cygwin, some objects like consoles wake up on "inappropriate" events
- * like mouse movements. we need to filter these with the "check" function
- */
- if ((unsigned)wait_ret < (unsigned)looper->htab_count)
- {
- for (hook = looper->hooks; hook; hook = hook->next)
- {
- if ( looper->htab[wait_ret] == hook->h &&
- (!hook->check || hook->check(hook)) )
- {
- D( "adb_win32: signaling %s for %x\n", hook->fh->name, hook->ready );
- event_hook_signal( hook );
- gotone = 1;
- break;
- }
- }
- }
- }
- }
- while (!gotone);
-
- for (hook = looper->hooks; hook; hook = hook->next) {
- if (hook->stop)
- hook->stop( hook );
- }
- }
-
- for (hook = looper->hooks; hook; hook = hook->next) {
- if (hook->peek && hook->peek(hook))
- event_hook_signal( hook );
- }
-}
-
-
-static void fdevent_register(fdevent *fde)
-{
- int fd = fde->fd - WIN32_FH_BASE;
-
- if(fd < 0) {
- FATAL("bogus negative fd (%d)\n", fde->fd);
- }
-
- if(fd >= fd_table_max) {
- int oldmax = fd_table_max;
- if(fde->fd > 32000) {
- FATAL("bogus huuuuge fd (%d)\n", fde->fd);
- }
- if(fd_table_max == 0) {
- fdevent_init();
- fd_table_max = 256;
- }
- while(fd_table_max <= fd) {
- fd_table_max *= 2;
- }
- fd_table = realloc(fd_table, sizeof(fdevent*) * fd_table_max);
- if(fd_table == 0) {
- FATAL("could not expand fd_table to %d entries\n", fd_table_max);
- }
- memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
- }
-
- fd_table[fd] = fde;
-}
-
-static void fdevent_unregister(fdevent *fde)
-{
- int fd = fde->fd - WIN32_FH_BASE;
-
- if((fd < 0) || (fd >= fd_table_max)) {
- FATAL("fd out of range (%d)\n", fde->fd);
- }
-
- if(fd_table[fd] != fde) {
- FATAL("fd_table out of sync");
- }
-
- fd_table[fd] = 0;
-
- if(!(fde->state & FDE_DONT_CLOSE)) {
- dump_fde(fde, "close");
- adb_close(fde->fd);
- }
-}
-
-static void fdevent_plist_enqueue(fdevent *node)
-{
- fdevent *list = &list_pending;
-
- node->next = list;
- node->prev = list->prev;
- node->prev->next = node;
- list->prev = node;
-}
-
-static void fdevent_plist_remove(fdevent *node)
-{
- node->prev->next = node->next;
- node->next->prev = node->prev;
- node->next = 0;
- node->prev = 0;
-}
-
-static fdevent *fdevent_plist_dequeue(void)
-{
- fdevent *list = &list_pending;
- fdevent *node = list->next;
-
- if(node == list) return 0;
-
- list->next = node->next;
- list->next->prev = list;
- node->next = 0;
- node->prev = 0;
-
- return node;
-}
-
-fdevent *fdevent_create(int fd, fd_func func, void *arg)
-{
- fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
- if(fde == 0) return 0;
- fdevent_install(fde, fd, func, arg);
- fde->state |= FDE_CREATED;
- return fde;
-}
-
-void fdevent_destroy(fdevent *fde)
-{
- if(fde == 0) return;
- if(!(fde->state & FDE_CREATED)) {
- FATAL("fde %p not created by fdevent_create()\n", fde);
- }
- fdevent_remove(fde);
-}
-
-void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
-{
- memset(fde, 0, sizeof(fdevent));
- fde->state = FDE_ACTIVE;
- fde->fd = fd;
- fde->func = func;
- fde->arg = arg;
-
- fdevent_register(fde);
- dump_fde(fde, "connect");
- fdevent_connect(fde);
- fde->state |= FDE_ACTIVE;
-}
-
-void fdevent_remove(fdevent *fde)
-{
- if(fde->state & FDE_PENDING) {
- fdevent_plist_remove(fde);
- }
-
- if(fde->state & FDE_ACTIVE) {
- fdevent_disconnect(fde);
- dump_fde(fde, "disconnect");
- fdevent_unregister(fde);
- }
-
- fde->state = 0;
- fde->events = 0;
-}
-
-
-void fdevent_set(fdevent *fde, unsigned events)
-{
- events &= FDE_EVENTMASK;
-
- if((fde->state & FDE_EVENTMASK) == (int)events) return;
-
- if(fde->state & FDE_ACTIVE) {
- fdevent_update(fde, events);
- dump_fde(fde, "update");
- }
-
- fde->state = (fde->state & FDE_STATEMASK) | events;
-
- if(fde->state & FDE_PENDING) {
- /* if we're pending, make sure
- ** we don't signal an event that
- ** is no longer wanted.
- */
- fde->events &= (~events);
- if(fde->events == 0) {
- fdevent_plist_remove(fde);
- fde->state &= (~FDE_PENDING);
- }
- }
-}
-
-void fdevent_add(fdevent *fde, unsigned events)
-{
- fdevent_set(
- fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
-}
-
-void fdevent_del(fdevent *fde, unsigned events)
-{
- fdevent_set(
- fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
-}
-
-void fdevent_loop()
-{
- fdevent *fde;
-
- for(;;) {
-#if DEBUG
- fprintf(stderr,"--- ---- waiting for events\n");
-#endif
- fdevent_process();
-
- while((fde = fdevent_plist_dequeue())) {
- unsigned events = fde->events;
- fde->events = 0;
- fde->state &= (~FDE_PENDING);
- dump_fde(fde, "callback");
- fde->func(fde->fd, events, fde->arg);
- }
- }
-}
-
-/** FILE EVENT HOOKS
- **/
-
-static void _event_file_prepare( EventHook hook )
-{
- if (hook->wanted & (FDE_READ|FDE_WRITE)) {
- /* we can always read/write */
- hook->ready |= hook->wanted & (FDE_READ|FDE_WRITE);
- }
-}
-
-static int _event_file_peek( EventHook hook )
-{
- return (hook->wanted & (FDE_READ|FDE_WRITE));
-}
-
-static void _fh_file_hook( FH f, int events, EventHook hook )
-{
- hook->h = f->fh_handle;
- hook->prepare = _event_file_prepare;
- hook->peek = _event_file_peek;
-}
-
-/** SOCKET EVENT HOOKS
- **/
-
-static void _event_socket_verify( EventHook hook, WSANETWORKEVENTS* evts )
-{
- if ( evts->lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE) ) {
- if (hook->wanted & FDE_READ)
- hook->ready |= FDE_READ;
- if ((evts->iErrorCode[FD_READ] != 0) && hook->wanted & FDE_ERROR)
- hook->ready |= FDE_ERROR;
- }
- if ( evts->lNetworkEvents & (FD_WRITE|FD_CONNECT|FD_CLOSE) ) {
- if (hook->wanted & FDE_WRITE)
- hook->ready |= FDE_WRITE;
- if ((evts->iErrorCode[FD_WRITE] != 0) && hook->wanted & FDE_ERROR)
- hook->ready |= FDE_ERROR;
- }
- if ( evts->lNetworkEvents & FD_OOB ) {
- if (hook->wanted & FDE_ERROR)
- hook->ready |= FDE_ERROR;
- }
-}
-
-static void _event_socket_prepare( EventHook hook )
-{
- WSANETWORKEVENTS evts;
-
- /* look if some of the events we want already happened ? */
- if (!WSAEnumNetworkEvents( hook->fh->fh_socket, NULL, &evts ))
- _event_socket_verify( hook, &evts );
-}
-
-static int _socket_wanted_to_flags( int wanted )
-{
- int flags = 0;
- if (wanted & FDE_READ)
- flags |= FD_READ | FD_ACCEPT | FD_CLOSE;
-
- if (wanted & FDE_WRITE)
- flags |= FD_WRITE | FD_CONNECT | FD_CLOSE;
-
- if (wanted & FDE_ERROR)
- flags |= FD_OOB;
-
- return flags;
-}
-
-static int _event_socket_start( EventHook hook )
-{
- /* create an event which we're going to wait for */
- FH fh = hook->fh;
- long flags = _socket_wanted_to_flags( hook->wanted );
-
- hook->h = fh->event;
- if (hook->h == INVALID_HANDLE_VALUE) {
- D( "_event_socket_start: no event for %s\n", fh->name );
- return 0;
- }
-
- if ( flags != fh->mask ) {
- D( "_event_socket_start: hooking %s for %x (flags %ld)\n", hook->fh->name, hook->wanted, flags );
- if ( WSAEventSelect( fh->fh_socket, hook->h, flags ) ) {
- D( "_event_socket_start: WSAEventSelect() for %s failed, error %d\n", hook->fh->name, WSAGetLastError() );
- CloseHandle( hook->h );
- hook->h = INVALID_HANDLE_VALUE;
- exit(1);
- return 0;
- }
- fh->mask = flags;
- }
- return 1;
-}
-
-static void _event_socket_stop( EventHook hook )
-{
- hook->h = INVALID_HANDLE_VALUE;
-}
-
-static int _event_socket_check( EventHook hook )
-{
- int result = 0;
- FH fh = hook->fh;
- WSANETWORKEVENTS evts;
-
- if (!WSAEnumNetworkEvents( fh->fh_socket, hook->h, &evts ) ) {
- _event_socket_verify( hook, &evts );
- result = (hook->ready != 0);
- if (result) {
- ResetEvent( hook->h );
- }
- }
- D( "_event_socket_check %s returns %d\n", fh->name, result );
- return result;
-}
-
-static int _event_socket_peek( EventHook hook )
-{
- WSANETWORKEVENTS evts;
- FH fh = hook->fh;
-
- /* look if some of the events we want already happened ? */
- if (!WSAEnumNetworkEvents( fh->fh_socket, NULL, &evts )) {
- _event_socket_verify( hook, &evts );
- if (hook->ready)
- ResetEvent( hook->h );
- }
-
- return hook->ready != 0;
-}
-
-
-
-static void _fh_socket_hook( FH f, int events, EventHook hook )
-{
- hook->prepare = _event_socket_prepare;
- hook->start = _event_socket_start;
- hook->stop = _event_socket_stop;
- hook->check = _event_socket_check;
- hook->peek = _event_socket_peek;
-
- _event_socket_start( hook );
-}
-
-/** SOCKETPAIR EVENT HOOKS
- **/
-
-static void _event_socketpair_prepare( EventHook hook )
-{
- FH fh = hook->fh;
- SocketPair pair = fh->fh_pair;
- BipBuffer rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip;
- BipBuffer wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip;
-
- if (hook->wanted & FDE_READ && rbip->can_read)
- hook->ready |= FDE_READ;
-
- if (hook->wanted & FDE_WRITE && wbip->can_write)
- hook->ready |= FDE_WRITE;
- }
-
- static int _event_socketpair_start( EventHook hook )
- {
- FH fh = hook->fh;
- SocketPair pair = fh->fh_pair;
- BipBuffer rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip;
- BipBuffer wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip;
-
- if (hook->wanted == FDE_READ)
- hook->h = rbip->evt_read;
-
- else if (hook->wanted == FDE_WRITE)
- hook->h = wbip->evt_write;
-
- else {
- D("_event_socketpair_start: can't handle FDE_READ+FDE_WRITE\n" );
- return 0;
- }
- D( "_event_socketpair_start: hook %s for %x wanted=%x\n",
- hook->fh->name, _fh_to_int(fh), hook->wanted);
- return 1;
-}
-
-static int _event_socketpair_peek( EventHook hook )
-{
- _event_socketpair_prepare( hook );
- return hook->ready != 0;
-}
-
-static void _fh_socketpair_hook( FH fh, int events, EventHook hook )
-{
- hook->prepare = _event_socketpair_prepare;
- hook->start = _event_socketpair_start;
- hook->peek = _event_socketpair_peek;
-}
-
-
-void
-adb_sysdeps_init( void )
-{
-#define ADB_MUTEX(x) InitializeCriticalSection( & x );
-#include "mutex_list.h"
- InitializeCriticalSection( &_win32_lock );
-}
-
-/* Windows doesn't have strtok_r. Use the one from bionic. */
-
-/*
- * Copyright (c) 1988 Regents of the University of California.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-char *
-adb_strtok_r(char *s, const char *delim, char **last)
-{
- char *spanp;
- int c, sc;
- char *tok;
-
-
- if (s == NULL && (s = *last) == NULL)
- return (NULL);
-
- /*
- * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
- */
-cont:
- c = *s++;
- for (spanp = (char *)delim; (sc = *spanp++) != 0;) {
- if (c == sc)
- goto cont;
- }
-
- if (c == 0) { /* no non-delimiter characters */
- *last = NULL;
- return (NULL);
- }
- tok = s - 1;
-
- /*
- * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
- * Note that delim must have one NUL; we stop if we see that, too.
- */
- for (;;) {
- c = *s++;
- spanp = (char *)delim;
- do {
- if ((sc = *spanp++) == c) {
- if (c == 0)
- s = NULL;
- else
- s[-1] = 0;
- *last = s;
- return (tok);
- }
- } while (sc != 0);
- }
- /* NOTREACHED */
-}
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
new file mode 100644
index 0000000..b7962b9
--- /dev/null
+++ b/adb/sysdeps_win32.cpp
@@ -0,0 +1,3056 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG TRACE_SYSDEPS
+
+// For whatever reason this blocks the definition of ToAscii...
+#undef NOGDI
+
+#include "sysdeps.h"
+
+#include <winsock2.h> /* winsock.h *must* be included before windows.h. */
+#include <windows.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "adb.h"
+
+extern void fatal(const char *fmt, ...);
+
+/* forward declarations */
+
+typedef const struct FHClassRec_* FHClass;
+typedef struct FHRec_* FH;
+typedef struct EventHookRec_* EventHook;
+
+typedef struct FHClassRec_ {
+ void (*_fh_init)(FH);
+ int (*_fh_close)(FH);
+ int (*_fh_lseek)(FH, int, int);
+ int (*_fh_read)(FH, void*, int);
+ int (*_fh_write)(FH, const void*, int);
+ void (*_fh_hook)(FH, int, EventHook);
+} FHClassRec;
+
+static void _fh_file_init(FH);
+static int _fh_file_close(FH);
+static int _fh_file_lseek(FH, int, int);
+static int _fh_file_read(FH, void*, int);
+static int _fh_file_write(FH, const void*, int);
+static void _fh_file_hook(FH, int, EventHook);
+
+static const FHClassRec _fh_file_class = {
+ _fh_file_init,
+ _fh_file_close,
+ _fh_file_lseek,
+ _fh_file_read,
+ _fh_file_write,
+ _fh_file_hook
+};
+
+static void _fh_socket_init(FH);
+static int _fh_socket_close(FH);
+static int _fh_socket_lseek(FH, int, int);
+static int _fh_socket_read(FH, void*, int);
+static int _fh_socket_write(FH, const void*, int);
+static void _fh_socket_hook(FH, int, EventHook);
+
+static const FHClassRec _fh_socket_class = {
+ _fh_socket_init,
+ _fh_socket_close,
+ _fh_socket_lseek,
+ _fh_socket_read,
+ _fh_socket_write,
+ _fh_socket_hook
+};
+
+#define assert(cond) do { if (!(cond)) fatal( "assertion failed '%s' on %s:%ld\n", #cond, __FILE__, __LINE__ ); } while (0)
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** replaces libs/cutils/load_file.c *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+void *load_file(const char *fn, unsigned *_sz)
+{
+ HANDLE file;
+ char *data;
+ DWORD file_size;
+
+ file = CreateFile( fn,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL );
+
+ if (file == INVALID_HANDLE_VALUE)
+ return NULL;
+
+ file_size = GetFileSize( file, NULL );
+ data = NULL;
+
+ if (file_size > 0) {
+ data = (char*) malloc( file_size + 1 );
+ if (data == NULL) {
+ D("load_file: could not allocate %ld bytes\n", file_size );
+ file_size = 0;
+ } else {
+ DWORD out_bytes;
+
+ if ( !ReadFile( file, data, file_size, &out_bytes, NULL ) ||
+ out_bytes != file_size )
+ {
+ D("load_file: could not read %ld bytes from '%s'\n", file_size, fn);
+ free(data);
+ data = NULL;
+ file_size = 0;
+ }
+ }
+ }
+ CloseHandle( file );
+
+ *_sz = (unsigned) file_size;
+ return data;
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** common file descriptor handling *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+/* used to emulate unix-domain socket pairs */
+typedef struct SocketPairRec_* SocketPair;
+
+typedef struct FHRec_
+{
+ FHClass clazz;
+ int used;
+ int eof;
+ union {
+ HANDLE handle;
+ SOCKET socket;
+ SocketPair pair;
+ } u;
+
+ HANDLE event;
+ int mask;
+
+ char name[32];
+
+} FHRec;
+
+#define fh_handle u.handle
+#define fh_socket u.socket
+#define fh_pair u.pair
+
+#define WIN32_FH_BASE 100
+
+#define WIN32_MAX_FHS 128
+
+static adb_mutex_t _win32_lock;
+static FHRec _win32_fhs[ WIN32_MAX_FHS ];
+static int _win32_fh_count;
+
+static FH
+_fh_from_int( int fd )
+{
+ FH f;
+
+ fd -= WIN32_FH_BASE;
+
+ if (fd < 0 || fd >= _win32_fh_count) {
+ D( "_fh_from_int: invalid fd %d\n", fd + WIN32_FH_BASE );
+ errno = EBADF;
+ return NULL;
+ }
+
+ f = &_win32_fhs[fd];
+
+ if (f->used == 0) {
+ D( "_fh_from_int: invalid fd %d\n", fd + WIN32_FH_BASE );
+ errno = EBADF;
+ return NULL;
+ }
+
+ return f;
+}
+
+
+static int
+_fh_to_int( FH f )
+{
+ if (f && f->used && f >= _win32_fhs && f < _win32_fhs + WIN32_MAX_FHS)
+ return (int)(f - _win32_fhs) + WIN32_FH_BASE;
+
+ return -1;
+}
+
+static FH
+_fh_alloc( FHClass clazz )
+{
+ int nn;
+ FH f = NULL;
+
+ adb_mutex_lock( &_win32_lock );
+
+ if (_win32_fh_count < WIN32_MAX_FHS) {
+ f = &_win32_fhs[ _win32_fh_count++ ];
+ goto Exit;
+ }
+
+ for (nn = 0; nn < WIN32_MAX_FHS; nn++) {
+ if ( _win32_fhs[nn].clazz == NULL) {
+ f = &_win32_fhs[nn];
+ goto Exit;
+ }
+ }
+ D( "_fh_alloc: no more free file descriptors\n" );
+Exit:
+ if (f) {
+ f->clazz = clazz;
+ f->used = 1;
+ f->eof = 0;
+ clazz->_fh_init(f);
+ }
+ adb_mutex_unlock( &_win32_lock );
+ return f;
+}
+
+
+static int
+_fh_close( FH f )
+{
+ if ( f->used ) {
+ f->clazz->_fh_close( f );
+ f->used = 0;
+ f->eof = 0;
+ f->clazz = NULL;
+ }
+ return 0;
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** file-based descriptor handling *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+static void _fh_file_init( FH f ) {
+ f->fh_handle = INVALID_HANDLE_VALUE;
+}
+
+static int _fh_file_close( FH f ) {
+ CloseHandle( f->fh_handle );
+ f->fh_handle = INVALID_HANDLE_VALUE;
+ return 0;
+}
+
+static int _fh_file_read( FH f, void* buf, int len ) {
+ DWORD read_bytes;
+
+ if ( !ReadFile( f->fh_handle, buf, (DWORD)len, &read_bytes, NULL ) ) {
+ D( "adb_read: could not read %d bytes from %s\n", len, f->name );
+ errno = EIO;
+ return -1;
+ } else if (read_bytes < (DWORD)len) {
+ f->eof = 1;
+ }
+ return (int)read_bytes;
+}
+
+static int _fh_file_write( FH f, const void* buf, int len ) {
+ DWORD wrote_bytes;
+
+ if ( !WriteFile( f->fh_handle, buf, (DWORD)len, &wrote_bytes, NULL ) ) {
+ D( "adb_file_write: could not write %d bytes from %s\n", len, f->name );
+ errno = EIO;
+ return -1;
+ } else if (wrote_bytes < (DWORD)len) {
+ f->eof = 1;
+ }
+ return (int)wrote_bytes;
+}
+
+static int _fh_file_lseek( FH f, int pos, int origin ) {
+ DWORD method;
+ DWORD result;
+
+ switch (origin)
+ {
+ case SEEK_SET: method = FILE_BEGIN; break;
+ case SEEK_CUR: method = FILE_CURRENT; break;
+ case SEEK_END: method = FILE_END; break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ result = SetFilePointer( f->fh_handle, pos, NULL, method );
+ if (result == INVALID_SET_FILE_POINTER) {
+ errno = EIO;
+ return -1;
+ } else {
+ f->eof = 0;
+ }
+ return (int)result;
+}
+
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** file-based descriptor handling *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+int adb_open(const char* path, int options)
+{
+ FH f;
+
+ DWORD desiredAccess = 0;
+ DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+
+ switch (options) {
+ case O_RDONLY:
+ desiredAccess = GENERIC_READ;
+ break;
+ case O_WRONLY:
+ desiredAccess = GENERIC_WRITE;
+ break;
+ case O_RDWR:
+ desiredAccess = GENERIC_READ | GENERIC_WRITE;
+ break;
+ default:
+ D("adb_open: invalid options (0x%0x)\n", options);
+ errno = EINVAL;
+ return -1;
+ }
+
+ f = _fh_alloc( &_fh_file_class );
+ if ( !f ) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ f->fh_handle = CreateFile( path, desiredAccess, shareMode, NULL, OPEN_EXISTING,
+ 0, NULL );
+
+ if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
+ _fh_close(f);
+ D( "adb_open: could not open '%s':", path );
+ switch (GetLastError()) {
+ case ERROR_FILE_NOT_FOUND:
+ D( "file not found\n" );
+ errno = ENOENT;
+ return -1;
+
+ case ERROR_PATH_NOT_FOUND:
+ D( "path not found\n" );
+ errno = ENOTDIR;
+ return -1;
+
+ default:
+ D( "unknown error\n" );
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
+ snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path );
+ D( "adb_open: '%s' => fd %d\n", path, _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+/* ignore mode on Win32 */
+int adb_creat(const char* path, int mode)
+{
+ FH f;
+
+ f = _fh_alloc( &_fh_file_class );
+ if ( !f ) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ f->fh_handle = CreateFile( path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
+ NULL );
+
+ if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
+ _fh_close(f);
+ D( "adb_creat: could not open '%s':", path );
+ switch (GetLastError()) {
+ case ERROR_FILE_NOT_FOUND:
+ D( "file not found\n" );
+ errno = ENOENT;
+ return -1;
+
+ case ERROR_PATH_NOT_FOUND:
+ D( "path not found\n" );
+ errno = ENOTDIR;
+ return -1;
+
+ default:
+ D( "unknown error\n" );
+ errno = ENOENT;
+ return -1;
+ }
+ }
+ snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path );
+ D( "adb_creat: '%s' => fd %d\n", path, _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+
+int adb_read(int fd, void* buf, int len)
+{
+ FH f = _fh_from_int(fd);
+
+ if (f == NULL) {
+ return -1;
+ }
+
+ return f->clazz->_fh_read( f, buf, len );
+}
+
+
+int adb_write(int fd, const void* buf, int len)
+{
+ FH f = _fh_from_int(fd);
+
+ if (f == NULL) {
+ return -1;
+ }
+
+ return f->clazz->_fh_write(f, buf, len);
+}
+
+
+int adb_lseek(int fd, int pos, int where)
+{
+ FH f = _fh_from_int(fd);
+
+ if (!f) {
+ return -1;
+ }
+
+ return f->clazz->_fh_lseek(f, pos, where);
+}
+
+
+int adb_shutdown(int fd)
+{
+ FH f = _fh_from_int(fd);
+
+ if (!f || f->clazz != &_fh_socket_class) {
+ D("adb_shutdown: invalid fd %d\n", fd);
+ return -1;
+ }
+
+ D( "adb_shutdown: %s\n", f->name);
+ shutdown( f->fh_socket, SD_BOTH );
+ return 0;
+}
+
+
+int adb_close(int fd)
+{
+ FH f = _fh_from_int(fd);
+
+ if (!f) {
+ return -1;
+ }
+
+ D( "adb_close: %s\n", f->name);
+ _fh_close(f);
+ return 0;
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** socket-based file descriptors *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+#undef setsockopt
+
+static void _socket_set_errno( void ) {
+ switch (WSAGetLastError()) {
+ case 0: errno = 0; break;
+ case WSAEWOULDBLOCK: errno = EAGAIN; break;
+ case WSAEINTR: errno = EINTR; break;
+ default:
+ D( "_socket_set_errno: unhandled value %d\n", WSAGetLastError() );
+ errno = EINVAL;
+ }
+}
+
+static void _fh_socket_init( FH f ) {
+ f->fh_socket = INVALID_SOCKET;
+ f->event = WSACreateEvent();
+ f->mask = 0;
+}
+
+static int _fh_socket_close( FH f ) {
+ /* gently tell any peer that we're closing the socket */
+ shutdown( f->fh_socket, SD_BOTH );
+ closesocket( f->fh_socket );
+ f->fh_socket = INVALID_SOCKET;
+ CloseHandle( f->event );
+ f->mask = 0;
+ return 0;
+}
+
+static int _fh_socket_lseek( FH f, int pos, int origin ) {
+ errno = EPIPE;
+ return -1;
+}
+
+static int _fh_socket_read(FH f, void* buf, int len) {
+ int result = recv(f->fh_socket, reinterpret_cast<char*>(buf), len, 0);
+ if (result == SOCKET_ERROR) {
+ _socket_set_errno();
+ result = -1;
+ }
+ return result;
+}
+
+static int _fh_socket_write(FH f, const void* buf, int len) {
+ int result = send(f->fh_socket, reinterpret_cast<const char*>(buf), len, 0);
+ if (result == SOCKET_ERROR) {
+ _socket_set_errno();
+ result = -1;
+ }
+ return result;
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** replacement for libs/cutils/socket_xxxx.c *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+#include <winsock2.h>
+
+static int _winsock_init;
+
+static void
+_cleanup_winsock( void )
+{
+ WSACleanup();
+}
+
+static void
+_init_winsock( void )
+{
+ if (!_winsock_init) {
+ WSADATA wsaData;
+ int rc = WSAStartup( MAKEWORD(2,2), &wsaData);
+ if (rc != 0) {
+ fatal( "adb: could not initialize Winsock\n" );
+ }
+ atexit( _cleanup_winsock );
+ _winsock_init = 1;
+ }
+}
+
+int socket_loopback_client(int port, int type)
+{
+ FH f = _fh_alloc( &_fh_socket_class );
+ struct sockaddr_in addr;
+ SOCKET s;
+
+ if (!f)
+ return -1;
+
+ if (!_winsock_init)
+ _init_winsock();
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ s = socket(AF_INET, type, 0);
+ if(s == INVALID_SOCKET) {
+ D("socket_loopback_client: could not create socket\n" );
+ _fh_close(f);
+ return -1;
+ }
+
+ f->fh_socket = s;
+ if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ D("socket_loopback_client: could not connect to %s:%d\n", type != SOCK_STREAM ? "udp" : "tcp", port );
+ _fh_close(f);
+ return -1;
+ }
+ snprintf( f->name, sizeof(f->name), "%d(lo-client:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
+ D( "socket_loopback_client: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+#define LISTEN_BACKLOG 4
+
+int socket_loopback_server(int port, int type)
+{
+ FH f = _fh_alloc( &_fh_socket_class );
+ struct sockaddr_in addr;
+ SOCKET s;
+ int n;
+
+ if (!f) {
+ return -1;
+ }
+
+ if (!_winsock_init)
+ _init_winsock();
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ s = socket(AF_INET, type, 0);
+ if(s == INVALID_SOCKET) return -1;
+
+ f->fh_socket = s;
+
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n));
+
+ if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ _fh_close(f);
+ return -1;
+ }
+ if (type == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+ if (ret < 0) {
+ _fh_close(f);
+ return -1;
+ }
+ }
+ snprintf( f->name, sizeof(f->name), "%d(lo-server:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
+ D( "socket_loopback_server: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+
+int socket_network_client(const char *host, int port, int type)
+{
+ FH f = _fh_alloc( &_fh_socket_class );
+ struct hostent *hp;
+ struct sockaddr_in addr;
+ SOCKET s;
+
+ if (!f)
+ return -1;
+
+ if (!_winsock_init)
+ _init_winsock();
+
+ hp = gethostbyname(host);
+ if(hp == 0) {
+ _fh_close(f);
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = hp->h_addrtype;
+ addr.sin_port = htons(port);
+ memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
+
+ s = socket(hp->h_addrtype, type, 0);
+ if(s == INVALID_SOCKET) {
+ _fh_close(f);
+ return -1;
+ }
+ f->fh_socket = s;
+
+ if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ _fh_close(f);
+ return -1;
+ }
+
+ snprintf( f->name, sizeof(f->name), "%d(net-client:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
+ D( "socket_network_client: host '%s' port %d type %s => fd %d\n", host, port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+
+int socket_network_client_timeout(const char *host, int port, int type, int timeout)
+{
+ // TODO: implement timeouts for Windows.
+ return socket_network_client(host, port, type);
+}
+
+
+int socket_inaddr_any_server(int port, int type)
+{
+ FH f = _fh_alloc( &_fh_socket_class );
+ struct sockaddr_in addr;
+ SOCKET s;
+ int n;
+
+ if (!f)
+ return -1;
+
+ if (!_winsock_init)
+ _init_winsock();
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ s = socket(AF_INET, type, 0);
+ if(s == INVALID_SOCKET) {
+ _fh_close(f);
+ return -1;
+ }
+
+ f->fh_socket = s;
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n));
+
+ if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ _fh_close(f);
+ return -1;
+ }
+
+ if (type == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+ if (ret < 0) {
+ _fh_close(f);
+ return -1;
+ }
+ }
+ snprintf( f->name, sizeof(f->name), "%d(any-server:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
+ D( "socket_inaddr_server: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+#undef accept
+int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen)
+{
+ FH serverfh = _fh_from_int(serverfd);
+ FH fh;
+
+ if ( !serverfh || serverfh->clazz != &_fh_socket_class ) {
+ D( "adb_socket_accept: invalid fd %d\n", serverfd );
+ return -1;
+ }
+
+ fh = _fh_alloc( &_fh_socket_class );
+ if (!fh) {
+ D( "adb_socket_accept: not enough memory to allocate accepted socket descriptor\n" );
+ return -1;
+ }
+
+ fh->fh_socket = accept( serverfh->fh_socket, addr, addrlen );
+ if (fh->fh_socket == INVALID_SOCKET) {
+ _fh_close( fh );
+ D( "adb_socket_accept: accept on fd %d return error %ld\n", serverfd, GetLastError() );
+ return -1;
+ }
+
+ snprintf( fh->name, sizeof(fh->name), "%d(accept:%s)", _fh_to_int(fh), serverfh->name );
+ D( "adb_socket_accept on fd %d returns fd %d\n", serverfd, _fh_to_int(fh) );
+ return _fh_to_int(fh);
+}
+
+
+int adb_setsockopt( int fd, int level, int optname, const void* optval, socklen_t optlen )
+{
+ FH fh = _fh_from_int(fd);
+
+ if ( !fh || fh->clazz != &_fh_socket_class ) {
+ D("adb_setsockopt: invalid fd %d\n", fd);
+ return -1;
+ }
+
+ return setsockopt( fh->fh_socket, level, optname, reinterpret_cast<const char*>(optval), optlen );
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** emulated socketpairs *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+/* we implement socketpairs directly in use space for the following reasons:
+ * - it avoids copying data from/to the Nt kernel
+ * - it allows us to implement fdevent hooks easily and cheaply, something
+ * that is not possible with standard Win32 pipes !!
+ *
+ * basically, we use two circular buffers, each one corresponding to a given
+ * direction.
+ *
+ * each buffer is implemented as two regions:
+ *
+ * region A which is (a_start,a_end)
+ * region B which is (0, b_end) with b_end <= a_start
+ *
+ * an empty buffer has: a_start = a_end = b_end = 0
+ *
+ * a_start is the pointer where we start reading data
+ * a_end is the pointer where we start writing data, unless it is BUFFER_SIZE,
+ * then you start writing at b_end
+ *
+ * the buffer is full when b_end == a_start && a_end == BUFFER_SIZE
+ *
+ * there is room when b_end < a_start || a_end < BUFER_SIZE
+ *
+ * when reading, a_start is incremented, it a_start meets a_end, then
+ * we do: a_start = 0, a_end = b_end, b_end = 0, and keep going on..
+ */
+
+#define BIP_BUFFER_SIZE 4096
+
+#if 0
+#include <stdio.h>
+# define BIPD(x) D x
+# define BIPDUMP bip_dump_hex
+
+static void bip_dump_hex( const unsigned char* ptr, size_t len )
+{
+ int nn, len2 = len;
+
+ if (len2 > 8) len2 = 8;
+
+ for (nn = 0; nn < len2; nn++)
+ printf("%02x", ptr[nn]);
+ printf(" ");
+
+ for (nn = 0; nn < len2; nn++) {
+ int c = ptr[nn];
+ if (c < 32 || c > 127)
+ c = '.';
+ printf("%c", c);
+ }
+ printf("\n");
+ fflush(stdout);
+}
+
+#else
+# define BIPD(x) do {} while (0)
+# define BIPDUMP(p,l) BIPD(p)
+#endif
+
+typedef struct BipBufferRec_
+{
+ int a_start;
+ int a_end;
+ int b_end;
+ int fdin;
+ int fdout;
+ int closed;
+ int can_write; /* boolean */
+ HANDLE evt_write; /* event signaled when one can write to a buffer */
+ int can_read; /* boolean */
+ HANDLE evt_read; /* event signaled when one can read from a buffer */
+ CRITICAL_SECTION lock;
+ unsigned char buff[ BIP_BUFFER_SIZE ];
+
+} BipBufferRec, *BipBuffer;
+
+static void
+bip_buffer_init( BipBuffer buffer )
+{
+ D( "bit_buffer_init %p\n", buffer );
+ buffer->a_start = 0;
+ buffer->a_end = 0;
+ buffer->b_end = 0;
+ buffer->can_write = 1;
+ buffer->can_read = 0;
+ buffer->fdin = 0;
+ buffer->fdout = 0;
+ buffer->closed = 0;
+ buffer->evt_write = CreateEvent( NULL, TRUE, TRUE, NULL );
+ buffer->evt_read = CreateEvent( NULL, TRUE, FALSE, NULL );
+ InitializeCriticalSection( &buffer->lock );
+}
+
+static void
+bip_buffer_close( BipBuffer bip )
+{
+ bip->closed = 1;
+
+ if (!bip->can_read) {
+ SetEvent( bip->evt_read );
+ }
+ if (!bip->can_write) {
+ SetEvent( bip->evt_write );
+ }
+}
+
+static void
+bip_buffer_done( BipBuffer bip )
+{
+ BIPD(( "bip_buffer_done: %d->%d\n", bip->fdin, bip->fdout ));
+ CloseHandle( bip->evt_read );
+ CloseHandle( bip->evt_write );
+ DeleteCriticalSection( &bip->lock );
+}
+
+static int
+bip_buffer_write( BipBuffer bip, const void* src, int len )
+{
+ int avail, count = 0;
+
+ if (len <= 0)
+ return 0;
+
+ BIPD(( "bip_buffer_write: enter %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+ BIPDUMP( src, len );
+
+ EnterCriticalSection( &bip->lock );
+
+ while (!bip->can_write) {
+ int ret;
+ LeaveCriticalSection( &bip->lock );
+
+ if (bip->closed) {
+ errno = EPIPE;
+ return -1;
+ }
+ /* spinlocking here is probably unfair, but let's live with it */
+ ret = WaitForSingleObject( bip->evt_write, INFINITE );
+ if (ret != WAIT_OBJECT_0) { /* buffer probably closed */
+ D( "bip_buffer_write: error %d->%d WaitForSingleObject returned %d, error %ld\n", bip->fdin, bip->fdout, ret, GetLastError() );
+ return 0;
+ }
+ if (bip->closed) {
+ errno = EPIPE;
+ return -1;
+ }
+ EnterCriticalSection( &bip->lock );
+ }
+
+ BIPD(( "bip_buffer_write: exec %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+
+ avail = BIP_BUFFER_SIZE - bip->a_end;
+ if (avail > 0)
+ {
+ /* we can append to region A */
+ if (avail > len)
+ avail = len;
+
+ memcpy( bip->buff + bip->a_end, src, avail );
+ src = (const char *)src + avail;
+ count += avail;
+ len -= avail;
+
+ bip->a_end += avail;
+ if (bip->a_end == BIP_BUFFER_SIZE && bip->a_start == 0) {
+ bip->can_write = 0;
+ ResetEvent( bip->evt_write );
+ goto Exit;
+ }
+ }
+
+ if (len == 0)
+ goto Exit;
+
+ avail = bip->a_start - bip->b_end;
+ assert( avail > 0 ); /* since can_write is TRUE */
+
+ if (avail > len)
+ avail = len;
+
+ memcpy( bip->buff + bip->b_end, src, avail );
+ count += avail;
+ bip->b_end += avail;
+
+ if (bip->b_end == bip->a_start) {
+ bip->can_write = 0;
+ ResetEvent( bip->evt_write );
+ }
+
+Exit:
+ assert( count > 0 );
+
+ if ( !bip->can_read ) {
+ bip->can_read = 1;
+ SetEvent( bip->evt_read );
+ }
+
+ BIPD(( "bip_buffer_write: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n",
+ bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read ));
+ LeaveCriticalSection( &bip->lock );
+
+ return count;
+ }
+
+static int
+bip_buffer_read( BipBuffer bip, void* dst, int len )
+{
+ int avail, count = 0;
+
+ if (len <= 0)
+ return 0;
+
+ BIPD(( "bip_buffer_read: enter %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+
+ EnterCriticalSection( &bip->lock );
+ while ( !bip->can_read )
+ {
+#if 0
+ LeaveCriticalSection( &bip->lock );
+ errno = EAGAIN;
+ return -1;
+#else
+ int ret;
+ LeaveCriticalSection( &bip->lock );
+
+ if (bip->closed) {
+ errno = EPIPE;
+ return -1;
+ }
+
+ ret = WaitForSingleObject( bip->evt_read, INFINITE );
+ if (ret != WAIT_OBJECT_0) { /* probably closed buffer */
+ D( "bip_buffer_read: error %d->%d WaitForSingleObject returned %d, error %ld\n", bip->fdin, bip->fdout, ret, GetLastError());
+ return 0;
+ }
+ if (bip->closed) {
+ errno = EPIPE;
+ return -1;
+ }
+ EnterCriticalSection( &bip->lock );
+#endif
+ }
+
+ BIPD(( "bip_buffer_read: exec %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+
+ avail = bip->a_end - bip->a_start;
+ assert( avail > 0 ); /* since can_read is TRUE */
+
+ if (avail > len)
+ avail = len;
+
+ memcpy( dst, bip->buff + bip->a_start, avail );
+ dst = (char *)dst + avail;
+ count += avail;
+ len -= avail;
+
+ bip->a_start += avail;
+ if (bip->a_start < bip->a_end)
+ goto Exit;
+
+ bip->a_start = 0;
+ bip->a_end = bip->b_end;
+ bip->b_end = 0;
+
+ avail = bip->a_end;
+ if (avail > 0) {
+ if (avail > len)
+ avail = len;
+ memcpy( dst, bip->buff, avail );
+ count += avail;
+ bip->a_start += avail;
+
+ if ( bip->a_start < bip->a_end )
+ goto Exit;
+
+ bip->a_start = bip->a_end = 0;
+ }
+
+ bip->can_read = 0;
+ ResetEvent( bip->evt_read );
+
+Exit:
+ assert( count > 0 );
+
+ if (!bip->can_write ) {
+ bip->can_write = 1;
+ SetEvent( bip->evt_write );
+ }
+
+ BIPDUMP( (const unsigned char*)dst - count, count );
+ BIPD(( "bip_buffer_read: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n",
+ bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read ));
+ LeaveCriticalSection( &bip->lock );
+
+ return count;
+}
+
+typedef struct SocketPairRec_
+{
+ BipBufferRec a2b_bip;
+ BipBufferRec b2a_bip;
+ FH a_fd;
+ int used;
+
+} SocketPairRec;
+
+void _fh_socketpair_init( FH f )
+{
+ f->fh_pair = NULL;
+}
+
+static int
+_fh_socketpair_close( FH f )
+{
+ if ( f->fh_pair ) {
+ SocketPair pair = f->fh_pair;
+
+ if ( f == pair->a_fd ) {
+ pair->a_fd = NULL;
+ }
+
+ bip_buffer_close( &pair->b2a_bip );
+ bip_buffer_close( &pair->a2b_bip );
+
+ if ( --pair->used == 0 ) {
+ bip_buffer_done( &pair->b2a_bip );
+ bip_buffer_done( &pair->a2b_bip );
+ free( pair );
+ }
+ f->fh_pair = NULL;
+ }
+ return 0;
+}
+
+static int
+_fh_socketpair_lseek( FH f, int pos, int origin )
+{
+ errno = ESPIPE;
+ return -1;
+}
+
+static int
+_fh_socketpair_read( FH f, void* buf, int len )
+{
+ SocketPair pair = f->fh_pair;
+ BipBuffer bip;
+
+ if (!pair)
+ return -1;
+
+ if ( f == pair->a_fd )
+ bip = &pair->b2a_bip;
+ else
+ bip = &pair->a2b_bip;
+
+ return bip_buffer_read( bip, buf, len );
+}
+
+static int
+_fh_socketpair_write( FH f, const void* buf, int len )
+{
+ SocketPair pair = f->fh_pair;
+ BipBuffer bip;
+
+ if (!pair)
+ return -1;
+
+ if ( f == pair->a_fd )
+ bip = &pair->a2b_bip;
+ else
+ bip = &pair->b2a_bip;
+
+ return bip_buffer_write( bip, buf, len );
+}
+
+
+static void _fh_socketpair_hook( FH f, int event, EventHook hook ); /* forward */
+
+static const FHClassRec _fh_socketpair_class =
+{
+ _fh_socketpair_init,
+ _fh_socketpair_close,
+ _fh_socketpair_lseek,
+ _fh_socketpair_read,
+ _fh_socketpair_write,
+ _fh_socketpair_hook
+};
+
+
+int adb_socketpair(int sv[2]) {
+ SocketPair pair;
+
+ FH fa = _fh_alloc(&_fh_socketpair_class);
+ FH fb = _fh_alloc(&_fh_socketpair_class);
+
+ if (!fa || !fb)
+ goto Fail;
+
+ pair = reinterpret_cast<SocketPair>(malloc(sizeof(*pair)));
+ if (pair == NULL) {
+ D("adb_socketpair: not enough memory to allocate pipes\n" );
+ goto Fail;
+ }
+
+ bip_buffer_init( &pair->a2b_bip );
+ bip_buffer_init( &pair->b2a_bip );
+
+ fa->fh_pair = pair;
+ fb->fh_pair = pair;
+ pair->used = 2;
+ pair->a_fd = fa;
+
+ sv[0] = _fh_to_int(fa);
+ sv[1] = _fh_to_int(fb);
+
+ pair->a2b_bip.fdin = sv[0];
+ pair->a2b_bip.fdout = sv[1];
+ pair->b2a_bip.fdin = sv[1];
+ pair->b2a_bip.fdout = sv[0];
+
+ snprintf( fa->name, sizeof(fa->name), "%d(pair:%d)", sv[0], sv[1] );
+ snprintf( fb->name, sizeof(fb->name), "%d(pair:%d)", sv[1], sv[0] );
+ D( "adb_socketpair: returns (%d, %d)\n", sv[0], sv[1] );
+ return 0;
+
+Fail:
+ _fh_close(fb);
+ _fh_close(fa);
+ return -1;
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** fdevents emulation *****/
+/***** *****/
+/***** this is a very simple implementation, we rely on the fact *****/
+/***** that ADB doesn't use FDE_ERROR. *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+#define FATAL(x...) fatal(__FUNCTION__, x)
+
+#if DEBUG
+static void dump_fde(fdevent *fde, const char *info)
+{
+ fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
+ fde->state & FDE_READ ? 'R' : ' ',
+ fde->state & FDE_WRITE ? 'W' : ' ',
+ fde->state & FDE_ERROR ? 'E' : ' ',
+ info);
+}
+#else
+#define dump_fde(fde, info) do { } while(0)
+#endif
+
+#define FDE_EVENTMASK 0x00ff
+#define FDE_STATEMASK 0xff00
+
+#define FDE_ACTIVE 0x0100
+#define FDE_PENDING 0x0200
+#define FDE_CREATED 0x0400
+
+static void fdevent_plist_enqueue(fdevent *node);
+static void fdevent_plist_remove(fdevent *node);
+static fdevent *fdevent_plist_dequeue(void);
+
+static fdevent list_pending = {
+ .next = &list_pending,
+ .prev = &list_pending,
+};
+
+static fdevent **fd_table = 0;
+static int fd_table_max = 0;
+
+typedef struct EventLooperRec_* EventLooper;
+
+typedef struct EventHookRec_
+{
+ EventHook next;
+ FH fh;
+ HANDLE h;
+ int wanted; /* wanted event flags */
+ int ready; /* ready event flags */
+ void* aux;
+ void (*prepare)( EventHook hook );
+ int (*start) ( EventHook hook );
+ void (*stop) ( EventHook hook );
+ int (*check) ( EventHook hook );
+ int (*peek) ( EventHook hook );
+} EventHookRec;
+
+static EventHook _free_hooks;
+
+static EventHook
+event_hook_alloc(FH fh) {
+ EventHook hook = _free_hooks;
+ if (hook != NULL) {
+ _free_hooks = hook->next;
+ } else {
+ hook = reinterpret_cast<EventHook>(malloc(sizeof(*hook)));
+ if (hook == NULL)
+ fatal( "could not allocate event hook\n" );
+ }
+ hook->next = NULL;
+ hook->fh = fh;
+ hook->wanted = 0;
+ hook->ready = 0;
+ hook->h = INVALID_HANDLE_VALUE;
+ hook->aux = NULL;
+
+ hook->prepare = NULL;
+ hook->start = NULL;
+ hook->stop = NULL;
+ hook->check = NULL;
+ hook->peek = NULL;
+
+ return hook;
+}
+
+static void
+event_hook_free( EventHook hook )
+{
+ hook->fh = NULL;
+ hook->wanted = 0;
+ hook->ready = 0;
+ hook->next = _free_hooks;
+ _free_hooks = hook;
+}
+
+
+static void
+event_hook_signal( EventHook hook )
+{
+ FH f = hook->fh;
+ int fd = _fh_to_int(f);
+ fdevent* fde = fd_table[ fd - WIN32_FH_BASE ];
+
+ if (fde != NULL && fde->fd == fd) {
+ if ((fde->state & FDE_PENDING) == 0) {
+ fde->state |= FDE_PENDING;
+ fdevent_plist_enqueue( fde );
+ }
+ fde->events |= hook->wanted;
+ }
+}
+
+
+#define MAX_LOOPER_HANDLES WIN32_MAX_FHS
+
+typedef struct EventLooperRec_
+{
+ EventHook hooks;
+ HANDLE htab[ MAX_LOOPER_HANDLES ];
+ int htab_count;
+
+} EventLooperRec;
+
+static EventHook*
+event_looper_find_p( EventLooper looper, FH fh )
+{
+ EventHook *pnode = &looper->hooks;
+ EventHook node = *pnode;
+ for (;;) {
+ if ( node == NULL || node->fh == fh )
+ break;
+ pnode = &node->next;
+ node = *pnode;
+ }
+ return pnode;
+}
+
+static void
+event_looper_hook( EventLooper looper, int fd, int events )
+{
+ FH f = _fh_from_int(fd);
+ EventHook *pnode;
+ EventHook node;
+
+ if (f == NULL) /* invalid arg */ {
+ D("event_looper_hook: invalid fd=%d\n", fd);
+ return;
+ }
+
+ pnode = event_looper_find_p( looper, f );
+ node = *pnode;
+ if ( node == NULL ) {
+ node = event_hook_alloc( f );
+ node->next = *pnode;
+ *pnode = node;
+ }
+
+ if ( (node->wanted & events) != events ) {
+ /* this should update start/stop/check/peek */
+ D("event_looper_hook: call hook for %d (new=%x, old=%x)\n",
+ fd, node->wanted, events);
+ f->clazz->_fh_hook( f, events & ~node->wanted, node );
+ node->wanted |= events;
+ } else {
+ D("event_looper_hook: ignoring events %x for %d wanted=%x)\n",
+ events, fd, node->wanted);
+ }
+}
+
+static void
+event_looper_unhook( EventLooper looper, int fd, int events )
+{
+ FH fh = _fh_from_int(fd);
+ EventHook *pnode = event_looper_find_p( looper, fh );
+ EventHook node = *pnode;
+
+ if (node != NULL) {
+ int events2 = events & node->wanted;
+ if ( events2 == 0 ) {
+ D( "event_looper_unhook: events %x not registered for fd %d\n", events, fd );
+ return;
+ }
+ node->wanted &= ~events2;
+ if (!node->wanted) {
+ *pnode = node->next;
+ event_hook_free( node );
+ }
+ }
+}
+
+/*
+ * A fixer for WaitForMultipleObjects on condition that there are more than 64
+ * handles to wait on.
+ *
+ * In cetain cases DDMS may establish more than 64 connections with ADB. For
+ * instance, this may happen if there are more than 64 processes running on a
+ * device, or there are multiple devices connected (including the emulator) with
+ * the combined number of running processes greater than 64. In this case using
+ * WaitForMultipleObjects to wait on connection events simply wouldn't cut,
+ * because of the API limitations (64 handles max). So, we need to provide a way
+ * to scale WaitForMultipleObjects to accept an arbitrary number of handles. The
+ * easiest (and "Microsoft recommended") way to do that would be dividing the
+ * handle array into chunks with the chunk size less than 64, and fire up as many
+ * waiting threads as there are chunks. Then each thread would wait on a chunk of
+ * handles, and will report back to the caller which handle has been set.
+ * Here is the implementation of that algorithm.
+ */
+
+/* Number of handles to wait on in each wating thread. */
+#define WAIT_ALL_CHUNK_SIZE 63
+
+/* Descriptor for a wating thread */
+typedef struct WaitForAllParam {
+ /* A handle to an event to signal when waiting is over. This handle is shared
+ * accross all the waiting threads, so each waiting thread knows when any
+ * other thread has exited, so it can exit too. */
+ HANDLE main_event;
+ /* Upon exit from a waiting thread contains the index of the handle that has
+ * been signaled. The index is an absolute index of the signaled handle in
+ * the original array. This pointer is shared accross all the waiting threads
+ * and it's not guaranteed (due to a race condition) that when all the
+ * waiting threads exit, the value contained here would indicate the first
+ * handle that was signaled. This is fine, because the caller cares only
+ * about any handle being signaled. It doesn't care about the order, nor
+ * about the whole list of handles that were signaled. */
+ LONG volatile *signaled_index;
+ /* Array of handles to wait on in a waiting thread. */
+ HANDLE* handles;
+ /* Number of handles in 'handles' array to wait on. */
+ int handles_count;
+ /* Index inside the main array of the first handle in the 'handles' array. */
+ int first_handle_index;
+ /* Waiting thread handle. */
+ HANDLE thread;
+} WaitForAllParam;
+
+/* Waiting thread routine. */
+static unsigned __stdcall
+_in_waiter_thread(void* arg)
+{
+ HANDLE wait_on[WAIT_ALL_CHUNK_SIZE + 1];
+ int res;
+ WaitForAllParam* const param = (WaitForAllParam*)arg;
+
+ /* We have to wait on the main_event in order to be notified when any of the
+ * sibling threads is exiting. */
+ wait_on[0] = param->main_event;
+ /* The rest of the handles go behind the main event handle. */
+ memcpy(wait_on + 1, param->handles, param->handles_count * sizeof(HANDLE));
+
+ res = WaitForMultipleObjects(param->handles_count + 1, wait_on, FALSE, INFINITE);
+ if (res > 0 && res < (param->handles_count + 1)) {
+ /* One of the original handles got signaled. Save its absolute index into
+ * the output variable. */
+ InterlockedCompareExchange(param->signaled_index,
+ res - 1L + param->first_handle_index, -1L);
+ }
+
+ /* Notify the caller (and the siblings) that the wait is over. */
+ SetEvent(param->main_event);
+
+ _endthreadex(0);
+ return 0;
+}
+
+/* WaitForMultipeObjects fixer routine.
+ * Param:
+ * handles Array of handles to wait on.
+ * handles_count Number of handles in the array.
+ * Return:
+ * (>= 0 && < handles_count) - Index of the signaled handle in the array, or
+ * WAIT_FAILED on an error.
+ */
+static int
+_wait_for_all(HANDLE* handles, int handles_count)
+{
+ WaitForAllParam* threads;
+ HANDLE main_event;
+ int chunks, chunk, remains;
+
+ /* This variable is going to be accessed by several threads at the same time,
+ * this is bound to fail randomly when the core is run on multi-core machines.
+ * To solve this, we need to do the following (1 _and_ 2):
+ * 1. Use the "volatile" qualifier to ensure the compiler doesn't optimize
+ * out the reads/writes in this function unexpectedly.
+ * 2. Ensure correct memory ordering. The "simple" way to do that is to wrap
+ * all accesses inside a critical section. But we can also use
+ * InterlockedCompareExchange() which always provide a full memory barrier
+ * on Win32.
+ */
+ volatile LONG sig_index = -1;
+
+ /* Calculate number of chunks, and allocate thread param array. */
+ chunks = handles_count / WAIT_ALL_CHUNK_SIZE;
+ remains = handles_count % WAIT_ALL_CHUNK_SIZE;
+ threads = (WaitForAllParam*)malloc((chunks + (remains ? 1 : 0)) *
+ sizeof(WaitForAllParam));
+ if (threads == NULL) {
+ D("Unable to allocate thread array for %d handles.", handles_count);
+ return (int)WAIT_FAILED;
+ }
+
+ /* Create main event to wait on for all waiting threads. This is a "manualy
+ * reset" event that will remain set once it was set. */
+ main_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (main_event == NULL) {
+ D("Unable to create main event. Error: %d", (int)GetLastError());
+ free(threads);
+ return (int)WAIT_FAILED;
+ }
+
+ /*
+ * Initialize waiting thread parameters.
+ */
+
+ for (chunk = 0; chunk < chunks; chunk++) {
+ threads[chunk].main_event = main_event;
+ threads[chunk].signaled_index = &sig_index;
+ threads[chunk].first_handle_index = WAIT_ALL_CHUNK_SIZE * chunk;
+ threads[chunk].handles = handles + threads[chunk].first_handle_index;
+ threads[chunk].handles_count = WAIT_ALL_CHUNK_SIZE;
+ }
+ if (remains) {
+ threads[chunk].main_event = main_event;
+ threads[chunk].signaled_index = &sig_index;
+ threads[chunk].first_handle_index = WAIT_ALL_CHUNK_SIZE * chunk;
+ threads[chunk].handles = handles + threads[chunk].first_handle_index;
+ threads[chunk].handles_count = remains;
+ chunks++;
+ }
+
+ /* Start the waiting threads. */
+ for (chunk = 0; chunk < chunks; chunk++) {
+ /* Note that using adb_thread_create is not appropriate here, since we
+ * need a handle to wait on for thread termination. */
+ threads[chunk].thread = (HANDLE)_beginthreadex(NULL, 0, _in_waiter_thread,
+ &threads[chunk], 0, NULL);
+ if (threads[chunk].thread == NULL) {
+ /* Unable to create a waiter thread. Collapse. */
+ D("Unable to create a waiting thread %d of %d. errno=%d",
+ chunk, chunks, errno);
+ chunks = chunk;
+ SetEvent(main_event);
+ break;
+ }
+ }
+
+ /* Wait on any of the threads to get signaled. */
+ WaitForSingleObject(main_event, INFINITE);
+
+ /* Wait on all the waiting threads to exit. */
+ for (chunk = 0; chunk < chunks; chunk++) {
+ WaitForSingleObject(threads[chunk].thread, INFINITE);
+ CloseHandle(threads[chunk].thread);
+ }
+
+ CloseHandle(main_event);
+ free(threads);
+
+
+ const int ret = (int)InterlockedCompareExchange(&sig_index, -1, -1);
+ return (ret >= 0) ? ret : (int)WAIT_FAILED;
+}
+
+static EventLooperRec win32_looper;
+
+static void fdevent_init(void)
+{
+ win32_looper.htab_count = 0;
+ win32_looper.hooks = NULL;
+}
+
+static void fdevent_connect(fdevent *fde)
+{
+ EventLooper looper = &win32_looper;
+ int events = fde->state & FDE_EVENTMASK;
+
+ if (events != 0)
+ event_looper_hook( looper, fde->fd, events );
+}
+
+static void fdevent_disconnect(fdevent *fde)
+{
+ EventLooper looper = &win32_looper;
+ int events = fde->state & FDE_EVENTMASK;
+
+ if (events != 0)
+ event_looper_unhook( looper, fde->fd, events );
+}
+
+static void fdevent_update(fdevent *fde, unsigned events)
+{
+ EventLooper looper = &win32_looper;
+ unsigned events0 = fde->state & FDE_EVENTMASK;
+
+ if (events != events0) {
+ int removes = events0 & ~events;
+ int adds = events & ~events0;
+ if (removes) {
+ D("fdevent_update: remove %x from %d\n", removes, fde->fd);
+ event_looper_unhook( looper, fde->fd, removes );
+ }
+ if (adds) {
+ D("fdevent_update: add %x to %d\n", adds, fde->fd);
+ event_looper_hook ( looper, fde->fd, adds );
+ }
+ }
+}
+
+static void fdevent_process()
+{
+ EventLooper looper = &win32_looper;
+ EventHook hook;
+ int gotone = 0;
+
+ /* if we have at least one ready hook, execute it/them */
+ for (hook = looper->hooks; hook; hook = hook->next) {
+ hook->ready = 0;
+ if (hook->prepare) {
+ hook->prepare(hook);
+ if (hook->ready != 0) {
+ event_hook_signal( hook );
+ gotone = 1;
+ }
+ }
+ }
+
+ /* nothing's ready yet, so wait for something to happen */
+ if (!gotone)
+ {
+ looper->htab_count = 0;
+
+ for (hook = looper->hooks; hook; hook = hook->next)
+ {
+ if (hook->start && !hook->start(hook)) {
+ D( "fdevent_process: error when starting a hook\n" );
+ return;
+ }
+ if (hook->h != INVALID_HANDLE_VALUE) {
+ int nn;
+
+ for (nn = 0; nn < looper->htab_count; nn++)
+ {
+ if ( looper->htab[nn] == hook->h )
+ goto DontAdd;
+ }
+ looper->htab[ looper->htab_count++ ] = hook->h;
+ DontAdd:
+ ;
+ }
+ }
+
+ if (looper->htab_count == 0) {
+ D( "fdevent_process: nothing to wait for !!\n" );
+ return;
+ }
+
+ do
+ {
+ int wait_ret;
+
+ D( "adb_win32: waiting for %d events\n", looper->htab_count );
+ if (looper->htab_count > MAXIMUM_WAIT_OBJECTS) {
+ D("handle count %d exceeds MAXIMUM_WAIT_OBJECTS.\n", looper->htab_count);
+ wait_ret = _wait_for_all(looper->htab, looper->htab_count);
+ } else {
+ wait_ret = WaitForMultipleObjects( looper->htab_count, looper->htab, FALSE, INFINITE );
+ }
+ if (wait_ret == (int)WAIT_FAILED) {
+ D( "adb_win32: wait failed, error %ld\n", GetLastError() );
+ } else {
+ D( "adb_win32: got one (index %d)\n", wait_ret );
+
+ /* according to Cygwin, some objects like consoles wake up on "inappropriate" events
+ * like mouse movements. we need to filter these with the "check" function
+ */
+ if ((unsigned)wait_ret < (unsigned)looper->htab_count)
+ {
+ for (hook = looper->hooks; hook; hook = hook->next)
+ {
+ if ( looper->htab[wait_ret] == hook->h &&
+ (!hook->check || hook->check(hook)) )
+ {
+ D( "adb_win32: signaling %s for %x\n", hook->fh->name, hook->ready );
+ event_hook_signal( hook );
+ gotone = 1;
+ break;
+ }
+ }
+ }
+ }
+ }
+ while (!gotone);
+
+ for (hook = looper->hooks; hook; hook = hook->next) {
+ if (hook->stop)
+ hook->stop( hook );
+ }
+ }
+
+ for (hook = looper->hooks; hook; hook = hook->next) {
+ if (hook->peek && hook->peek(hook))
+ event_hook_signal( hook );
+ }
+}
+
+
+static void fdevent_register(fdevent *fde)
+{
+ int fd = fde->fd - WIN32_FH_BASE;
+
+ if(fd < 0) {
+ FATAL("bogus negative fd (%d)\n", fde->fd);
+ }
+
+ if(fd >= fd_table_max) {
+ int oldmax = fd_table_max;
+ if(fde->fd > 32000) {
+ FATAL("bogus huuuuge fd (%d)\n", fde->fd);
+ }
+ if(fd_table_max == 0) {
+ fdevent_init();
+ fd_table_max = 256;
+ }
+ while(fd_table_max <= fd) {
+ fd_table_max *= 2;
+ }
+ fd_table = reinterpret_cast<fdevent**>(realloc(fd_table, sizeof(fdevent*) * fd_table_max));
+ if(fd_table == 0) {
+ FATAL("could not expand fd_table to %d entries\n", fd_table_max);
+ }
+ memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
+ }
+
+ fd_table[fd] = fde;
+}
+
+static void fdevent_unregister(fdevent *fde)
+{
+ int fd = fde->fd - WIN32_FH_BASE;
+
+ if((fd < 0) || (fd >= fd_table_max)) {
+ FATAL("fd out of range (%d)\n", fde->fd);
+ }
+
+ if(fd_table[fd] != fde) {
+ FATAL("fd_table out of sync");
+ }
+
+ fd_table[fd] = 0;
+
+ if(!(fde->state & FDE_DONT_CLOSE)) {
+ dump_fde(fde, "close");
+ adb_close(fde->fd);
+ }
+}
+
+static void fdevent_plist_enqueue(fdevent *node)
+{
+ fdevent *list = &list_pending;
+
+ node->next = list;
+ node->prev = list->prev;
+ node->prev->next = node;
+ list->prev = node;
+}
+
+static void fdevent_plist_remove(fdevent *node)
+{
+ node->prev->next = node->next;
+ node->next->prev = node->prev;
+ node->next = 0;
+ node->prev = 0;
+}
+
+static fdevent *fdevent_plist_dequeue(void)
+{
+ fdevent *list = &list_pending;
+ fdevent *node = list->next;
+
+ if(node == list) return 0;
+
+ list->next = node->next;
+ list->next->prev = list;
+ node->next = 0;
+ node->prev = 0;
+
+ return node;
+}
+
+fdevent *fdevent_create(int fd, fd_func func, void *arg)
+{
+ fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
+ if(fde == 0) return 0;
+ fdevent_install(fde, fd, func, arg);
+ fde->state |= FDE_CREATED;
+ return fde;
+}
+
+void fdevent_destroy(fdevent *fde)
+{
+ if(fde == 0) return;
+ if(!(fde->state & FDE_CREATED)) {
+ FATAL("fde %p not created by fdevent_create()\n", fde);
+ }
+ fdevent_remove(fde);
+}
+
+void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
+{
+ memset(fde, 0, sizeof(fdevent));
+ fde->state = FDE_ACTIVE;
+ fde->fd = fd;
+ fde->func = func;
+ fde->arg = arg;
+
+ fdevent_register(fde);
+ dump_fde(fde, "connect");
+ fdevent_connect(fde);
+ fde->state |= FDE_ACTIVE;
+}
+
+void fdevent_remove(fdevent *fde)
+{
+ if(fde->state & FDE_PENDING) {
+ fdevent_plist_remove(fde);
+ }
+
+ if(fde->state & FDE_ACTIVE) {
+ fdevent_disconnect(fde);
+ dump_fde(fde, "disconnect");
+ fdevent_unregister(fde);
+ }
+
+ fde->state = 0;
+ fde->events = 0;
+}
+
+
+void fdevent_set(fdevent *fde, unsigned events)
+{
+ events &= FDE_EVENTMASK;
+
+ if((fde->state & FDE_EVENTMASK) == (int)events) return;
+
+ if(fde->state & FDE_ACTIVE) {
+ fdevent_update(fde, events);
+ dump_fde(fde, "update");
+ }
+
+ fde->state = (fde->state & FDE_STATEMASK) | events;
+
+ if(fde->state & FDE_PENDING) {
+ /* if we're pending, make sure
+ ** we don't signal an event that
+ ** is no longer wanted.
+ */
+ fde->events &= (~events);
+ if(fde->events == 0) {
+ fdevent_plist_remove(fde);
+ fde->state &= (~FDE_PENDING);
+ }
+ }
+}
+
+void fdevent_add(fdevent *fde, unsigned events)
+{
+ fdevent_set(
+ fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
+}
+
+void fdevent_del(fdevent *fde, unsigned events)
+{
+ fdevent_set(
+ fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
+}
+
+void fdevent_loop()
+{
+ fdevent *fde;
+
+ for(;;) {
+#if DEBUG
+ fprintf(stderr,"--- ---- waiting for events\n");
+#endif
+ fdevent_process();
+
+ while((fde = fdevent_plist_dequeue())) {
+ unsigned events = fde->events;
+ fde->events = 0;
+ fde->state &= (~FDE_PENDING);
+ dump_fde(fde, "callback");
+ fde->func(fde->fd, events, fde->arg);
+ }
+ }
+}
+
+/** FILE EVENT HOOKS
+ **/
+
+static void _event_file_prepare( EventHook hook )
+{
+ if (hook->wanted & (FDE_READ|FDE_WRITE)) {
+ /* we can always read/write */
+ hook->ready |= hook->wanted & (FDE_READ|FDE_WRITE);
+ }
+}
+
+static int _event_file_peek( EventHook hook )
+{
+ return (hook->wanted & (FDE_READ|FDE_WRITE));
+}
+
+static void _fh_file_hook( FH f, int events, EventHook hook )
+{
+ hook->h = f->fh_handle;
+ hook->prepare = _event_file_prepare;
+ hook->peek = _event_file_peek;
+}
+
+/** SOCKET EVENT HOOKS
+ **/
+
+static void _event_socket_verify( EventHook hook, WSANETWORKEVENTS* evts )
+{
+ if ( evts->lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE) ) {
+ if (hook->wanted & FDE_READ)
+ hook->ready |= FDE_READ;
+ if ((evts->iErrorCode[FD_READ] != 0) && hook->wanted & FDE_ERROR)
+ hook->ready |= FDE_ERROR;
+ }
+ if ( evts->lNetworkEvents & (FD_WRITE|FD_CONNECT|FD_CLOSE) ) {
+ if (hook->wanted & FDE_WRITE)
+ hook->ready |= FDE_WRITE;
+ if ((evts->iErrorCode[FD_WRITE] != 0) && hook->wanted & FDE_ERROR)
+ hook->ready |= FDE_ERROR;
+ }
+ if ( evts->lNetworkEvents & FD_OOB ) {
+ if (hook->wanted & FDE_ERROR)
+ hook->ready |= FDE_ERROR;
+ }
+}
+
+static void _event_socket_prepare( EventHook hook )
+{
+ WSANETWORKEVENTS evts;
+
+ /* look if some of the events we want already happened ? */
+ if (!WSAEnumNetworkEvents( hook->fh->fh_socket, NULL, &evts ))
+ _event_socket_verify( hook, &evts );
+}
+
+static int _socket_wanted_to_flags( int wanted )
+{
+ int flags = 0;
+ if (wanted & FDE_READ)
+ flags |= FD_READ | FD_ACCEPT | FD_CLOSE;
+
+ if (wanted & FDE_WRITE)
+ flags |= FD_WRITE | FD_CONNECT | FD_CLOSE;
+
+ if (wanted & FDE_ERROR)
+ flags |= FD_OOB;
+
+ return flags;
+}
+
+static int _event_socket_start( EventHook hook )
+{
+ /* create an event which we're going to wait for */
+ FH fh = hook->fh;
+ long flags = _socket_wanted_to_flags( hook->wanted );
+
+ hook->h = fh->event;
+ if (hook->h == INVALID_HANDLE_VALUE) {
+ D( "_event_socket_start: no event for %s\n", fh->name );
+ return 0;
+ }
+
+ if ( flags != fh->mask ) {
+ D( "_event_socket_start: hooking %s for %x (flags %ld)\n", hook->fh->name, hook->wanted, flags );
+ if ( WSAEventSelect( fh->fh_socket, hook->h, flags ) ) {
+ D( "_event_socket_start: WSAEventSelect() for %s failed, error %d\n", hook->fh->name, WSAGetLastError() );
+ CloseHandle( hook->h );
+ hook->h = INVALID_HANDLE_VALUE;
+ exit(1);
+ return 0;
+ }
+ fh->mask = flags;
+ }
+ return 1;
+}
+
+static void _event_socket_stop( EventHook hook )
+{
+ hook->h = INVALID_HANDLE_VALUE;
+}
+
+static int _event_socket_check( EventHook hook )
+{
+ int result = 0;
+ FH fh = hook->fh;
+ WSANETWORKEVENTS evts;
+
+ if (!WSAEnumNetworkEvents( fh->fh_socket, hook->h, &evts ) ) {
+ _event_socket_verify( hook, &evts );
+ result = (hook->ready != 0);
+ if (result) {
+ ResetEvent( hook->h );
+ }
+ }
+ D( "_event_socket_check %s returns %d\n", fh->name, result );
+ return result;
+}
+
+static int _event_socket_peek( EventHook hook )
+{
+ WSANETWORKEVENTS evts;
+ FH fh = hook->fh;
+
+ /* look if some of the events we want already happened ? */
+ if (!WSAEnumNetworkEvents( fh->fh_socket, NULL, &evts )) {
+ _event_socket_verify( hook, &evts );
+ if (hook->ready)
+ ResetEvent( hook->h );
+ }
+
+ return hook->ready != 0;
+}
+
+
+
+static void _fh_socket_hook( FH f, int events, EventHook hook )
+{
+ hook->prepare = _event_socket_prepare;
+ hook->start = _event_socket_start;
+ hook->stop = _event_socket_stop;
+ hook->check = _event_socket_check;
+ hook->peek = _event_socket_peek;
+
+ _event_socket_start( hook );
+}
+
+/** SOCKETPAIR EVENT HOOKS
+ **/
+
+static void _event_socketpair_prepare( EventHook hook )
+{
+ FH fh = hook->fh;
+ SocketPair pair = fh->fh_pair;
+ BipBuffer rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip;
+ BipBuffer wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip;
+
+ if (hook->wanted & FDE_READ && rbip->can_read)
+ hook->ready |= FDE_READ;
+
+ if (hook->wanted & FDE_WRITE && wbip->can_write)
+ hook->ready |= FDE_WRITE;
+ }
+
+ static int _event_socketpair_start( EventHook hook )
+ {
+ FH fh = hook->fh;
+ SocketPair pair = fh->fh_pair;
+ BipBuffer rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip;
+ BipBuffer wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip;
+
+ if (hook->wanted == FDE_READ)
+ hook->h = rbip->evt_read;
+
+ else if (hook->wanted == FDE_WRITE)
+ hook->h = wbip->evt_write;
+
+ else {
+ D("_event_socketpair_start: can't handle FDE_READ+FDE_WRITE\n" );
+ return 0;
+ }
+ D( "_event_socketpair_start: hook %s for %x wanted=%x\n",
+ hook->fh->name, _fh_to_int(fh), hook->wanted);
+ return 1;
+}
+
+static int _event_socketpair_peek( EventHook hook )
+{
+ _event_socketpair_prepare( hook );
+ return hook->ready != 0;
+}
+
+static void _fh_socketpair_hook( FH fh, int events, EventHook hook )
+{
+ hook->prepare = _event_socketpair_prepare;
+ hook->start = _event_socketpair_start;
+ hook->peek = _event_socketpair_peek;
+}
+
+
+void
+adb_sysdeps_init( void )
+{
+#define ADB_MUTEX(x) InitializeCriticalSection( & x );
+#include "mutex_list.h"
+ InitializeCriticalSection( &_win32_lock );
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** Console Window Terminal Emulation *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+// This reads input from a Win32 console window and translates it into Unix
+// terminal-style sequences. This emulates mostly Gnome Terminal (in Normal
+// mode, not Application mode), which itself emulates xterm. Gnome Terminal
+// is emulated instead of xterm because it is probably more popular than xterm:
+// Ubuntu's default Ctrl-Alt-T shortcut opens Gnome Terminal, Gnome Terminal
+// supports modern fonts, etc. It seems best to emulate the terminal that most
+// Android developers use because they'll fix apps (the shell, etc.) to keep
+// working with that terminal's emulation.
+//
+// The point of this emulation is not to be perfect or to solve all issues with
+// console windows on Windows, but to be better than the original code which
+// just called read() (which called ReadFile(), which called ReadConsoleA())
+// which did not support Ctrl-C, tab completion, shell input line editing
+// keys, server echo, and more.
+//
+// This implementation reconfigures the console with SetConsoleMode(), then
+// calls ReadConsoleInput() to get raw input which it remaps to Unix
+// terminal-style sequences which is returned via unix_read() which is used
+// by the 'adb shell' command.
+//
+// Code organization:
+//
+// * stdin_raw_init() and stdin_raw_restore() reconfigure the console.
+// * unix_read() detects console windows (as opposed to pipes, files, etc.).
+// * _console_read() is the main code of the emulation.
+
+
+// Read an input record from the console; one that should be processed.
+static bool _get_interesting_input_record_uncached(const HANDLE console,
+ INPUT_RECORD* const input_record) {
+ for (;;) {
+ DWORD read_count = 0;
+ memset(input_record, 0, sizeof(*input_record));
+ if (!ReadConsoleInputA(console, input_record, 1, &read_count)) {
+ D("_get_interesting_input_record_uncached: ReadConsoleInputA() "
+ "failure, error %ld\n", GetLastError());
+ errno = EIO;
+ return false;
+ }
+
+ if (read_count == 0) { // should be impossible
+ fatal("ReadConsoleInputA returned 0");
+ }
+
+ if (read_count != 1) { // should be impossible
+ fatal("ReadConsoleInputA did not return one input record");
+ }
+
+ if ((input_record->EventType == KEY_EVENT) &&
+ (input_record->Event.KeyEvent.bKeyDown)) {
+ if (input_record->Event.KeyEvent.wRepeatCount == 0) {
+ fatal("ReadConsoleInputA returned a key event with zero repeat"
+ " count");
+ }
+
+ // Got an interesting INPUT_RECORD, so return
+ return true;
+ }
+ }
+}
+
+// Cached input record (in case _console_read() is passed a buffer that doesn't
+// have enough space to fit wRepeatCount number of key sequences). A non-zero
+// wRepeatCount indicates that a record is cached.
+static INPUT_RECORD _win32_input_record;
+
+// Get the next KEY_EVENT_RECORD that should be processed.
+static KEY_EVENT_RECORD* _get_key_event_record(const HANDLE console) {
+ // If nothing cached, read directly from the console until we get an
+ // interesting record.
+ if (_win32_input_record.Event.KeyEvent.wRepeatCount == 0) {
+ if (!_get_interesting_input_record_uncached(console,
+ &_win32_input_record)) {
+ // There was an error, so make sure wRepeatCount is zero because
+ // that signifies no cached input record.
+ _win32_input_record.Event.KeyEvent.wRepeatCount = 0;
+ return NULL;
+ }
+ }
+
+ return &_win32_input_record.Event.KeyEvent;
+}
+
+static __inline__ bool _is_shift_pressed(const DWORD control_key_state) {
+ return (control_key_state & SHIFT_PRESSED) != 0;
+}
+
+static __inline__ bool _is_ctrl_pressed(const DWORD control_key_state) {
+ return (control_key_state & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) != 0;
+}
+
+static __inline__ bool _is_alt_pressed(const DWORD control_key_state) {
+ return (control_key_state & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) != 0;
+}
+
+static __inline__ bool _is_numlock_on(const DWORD control_key_state) {
+ return (control_key_state & NUMLOCK_ON) != 0;
+}
+
+static __inline__ bool _is_capslock_on(const DWORD control_key_state) {
+ return (control_key_state & CAPSLOCK_ON) != 0;
+}
+
+static __inline__ bool _is_enhanced_key(const DWORD control_key_state) {
+ return (control_key_state & ENHANCED_KEY) != 0;
+}
+
+// Constants from MSDN for ToAscii().
+static const BYTE TOASCII_KEY_OFF = 0x00;
+static const BYTE TOASCII_KEY_DOWN = 0x80;
+static const BYTE TOASCII_KEY_TOGGLED_ON = 0x01; // for CapsLock
+
+// Given a key event, ignore a modifier key and return the character that was
+// entered without the modifier. Writes to *ch and returns the number of bytes
+// written.
+static size_t _get_char_ignoring_modifier(char* const ch,
+ const KEY_EVENT_RECORD* const key_event, const DWORD control_key_state,
+ const WORD modifier) {
+ // If there is no character from Windows, try ignoring the specified
+ // modifier and look for a character. Note that if AltGr is being used,
+ // there will be a character from Windows.
+ if (key_event->uChar.AsciiChar == '\0') {
+ // Note that we read the control key state from the passed in argument
+ // instead of from key_event since the argument has been normalized.
+ if (((modifier == VK_SHIFT) &&
+ _is_shift_pressed(control_key_state)) ||
+ ((modifier == VK_CONTROL) &&
+ _is_ctrl_pressed(control_key_state)) ||
+ ((modifier == VK_MENU) && _is_alt_pressed(control_key_state))) {
+
+ BYTE key_state[256] = {0};
+ key_state[VK_SHIFT] = _is_shift_pressed(control_key_state) ?
+ TOASCII_KEY_DOWN : TOASCII_KEY_OFF;
+ key_state[VK_CONTROL] = _is_ctrl_pressed(control_key_state) ?
+ TOASCII_KEY_DOWN : TOASCII_KEY_OFF;
+ key_state[VK_MENU] = _is_alt_pressed(control_key_state) ?
+ TOASCII_KEY_DOWN : TOASCII_KEY_OFF;
+ key_state[VK_CAPITAL] = _is_capslock_on(control_key_state) ?
+ TOASCII_KEY_TOGGLED_ON : TOASCII_KEY_OFF;
+
+ // cause this modifier to be ignored
+ key_state[modifier] = TOASCII_KEY_OFF;
+
+ WORD translated = 0;
+ if (ToAscii(key_event->wVirtualKeyCode,
+ key_event->wVirtualScanCode, key_state, &translated, 0) == 1) {
+ // Ignoring the modifier, we found a character.
+ *ch = (CHAR)translated;
+ return 1;
+ }
+ }
+ }
+
+ // Just use whatever Windows told us originally.
+ *ch = key_event->uChar.AsciiChar;
+
+ // If the character from Windows is NULL, return a size of zero.
+ return (*ch == '\0') ? 0 : 1;
+}
+
+// If a Ctrl key is pressed, lookup the character, ignoring the Ctrl key,
+// but taking into account the shift key. This is because for a sequence like
+// Ctrl-Alt-0, we want to find the character '0' and for Ctrl-Alt-Shift-0,
+// we want to find the character ')'.
+//
+// Note that Windows doesn't seem to pass bKeyDown for Ctrl-Shift-NoAlt-0
+// because it is the default key-sequence to switch the input language.
+// This is configurable in the Region and Language control panel.
+static __inline__ size_t _get_non_control_char(char* const ch,
+ const KEY_EVENT_RECORD* const key_event, const DWORD control_key_state) {
+ return _get_char_ignoring_modifier(ch, key_event, control_key_state,
+ VK_CONTROL);
+}
+
+// Get without Alt.
+static __inline__ size_t _get_non_alt_char(char* const ch,
+ const KEY_EVENT_RECORD* const key_event, const DWORD control_key_state) {
+ return _get_char_ignoring_modifier(ch, key_event, control_key_state,
+ VK_MENU);
+}
+
+// Ignore the control key, find the character from Windows, and apply any
+// Control key mappings (for example, Ctrl-2 is a NULL character). Writes to
+// *pch and returns number of bytes written.
+static size_t _get_control_character(char* const pch,
+ const KEY_EVENT_RECORD* const key_event, const DWORD control_key_state) {
+ const size_t len = _get_non_control_char(pch, key_event,
+ control_key_state);
+
+ if ((len == 1) && _is_ctrl_pressed(control_key_state)) {
+ char ch = *pch;
+ switch (ch) {
+ case '2':
+ case '@':
+ case '`':
+ ch = '\0';
+ break;
+ case '3':
+ case '[':
+ case '{':
+ ch = '\x1b';
+ break;
+ case '4':
+ case '\\':
+ case '|':
+ ch = '\x1c';
+ break;
+ case '5':
+ case ']':
+ case '}':
+ ch = '\x1d';
+ break;
+ case '6':
+ case '^':
+ case '~':
+ ch = '\x1e';
+ break;
+ case '7':
+ case '-':
+ case '_':
+ ch = '\x1f';
+ break;
+ case '8':
+ ch = '\x7f';
+ break;
+ case '/':
+ if (!_is_alt_pressed(control_key_state)) {
+ ch = '\x1f';
+ }
+ break;
+ case '?':
+ if (!_is_alt_pressed(control_key_state)) {
+ ch = '\x7f';
+ }
+ break;
+ }
+ *pch = ch;
+ }
+
+ return len;
+}
+
+static DWORD _normalize_altgr_control_key_state(
+ const KEY_EVENT_RECORD* const key_event) {
+ DWORD control_key_state = key_event->dwControlKeyState;
+
+ // If we're in an AltGr situation where the AltGr key is down (depending on
+ // the keyboard layout, that might be the physical right alt key which
+ // produces a control_key_state where Right-Alt and Left-Ctrl are down) or
+ // AltGr-equivalent keys are down (any Ctrl key + any Alt key), and we have
+ // a character (which indicates that there was an AltGr mapping), then act
+ // as if alt and control are not really down for the purposes of modifiers.
+ // This makes it so that if the user with, say, a German keyboard layout
+ // presses AltGr-] (which we see as Right-Alt + Left-Ctrl + key), we just
+ // output the key and we don't see the Alt and Ctrl keys.
+ if (_is_ctrl_pressed(control_key_state) &&
+ _is_alt_pressed(control_key_state)
+ && (key_event->uChar.AsciiChar != '\0')) {
+ // Try to remove as few bits as possible to improve our chances of
+ // detecting combinations like Left-Alt + AltGr, Right-Ctrl + AltGr, or
+ // Left-Alt + Right-Ctrl + AltGr.
+ if ((control_key_state & RIGHT_ALT_PRESSED) != 0) {
+ // Remove Right-Alt.
+ control_key_state &= ~RIGHT_ALT_PRESSED;
+ // If uChar is set, a Ctrl key is pressed, and Right-Alt is
+ // pressed, Left-Ctrl is almost always set, except if the user
+ // presses Right-Ctrl, then AltGr (in that specific order) for
+ // whatever reason. At any rate, make sure the bit is not set.
+ control_key_state &= ~LEFT_CTRL_PRESSED;
+ } else if ((control_key_state & LEFT_ALT_PRESSED) != 0) {
+ // Remove Left-Alt.
+ control_key_state &= ~LEFT_ALT_PRESSED;
+ // Whichever Ctrl key is down, remove it from the state. We only
+ // remove one key, to improve our chances of detecting the
+ // corner-case of Left-Ctrl + Left-Alt + Right-Ctrl.
+ if ((control_key_state & LEFT_CTRL_PRESSED) != 0) {
+ // Remove Left-Ctrl.
+ control_key_state &= ~LEFT_CTRL_PRESSED;
+ } else if ((control_key_state & RIGHT_CTRL_PRESSED) != 0) {
+ // Remove Right-Ctrl.
+ control_key_state &= ~RIGHT_CTRL_PRESSED;
+ }
+ }
+
+ // Note that this logic isn't 100% perfect because Windows doesn't
+ // allow us to detect all combinations because a physical AltGr key
+ // press shows up as two bits, plus some combinations are ambiguous
+ // about what is actually physically pressed.
+ }
+
+ return control_key_state;
+}
+
+// If NumLock is on and Shift is pressed, SHIFT_PRESSED is not set in
+// dwControlKeyState for the following keypad keys: period, 0-9. If we detect
+// this scenario, set the SHIFT_PRESSED bit so we can add modifiers
+// appropriately.
+static DWORD _normalize_keypad_control_key_state(const WORD vk,
+ const DWORD control_key_state) {
+ if (!_is_numlock_on(control_key_state)) {
+ return control_key_state;
+ }
+ if (!_is_enhanced_key(control_key_state)) {
+ switch (vk) {
+ case VK_INSERT: // 0
+ case VK_DELETE: // .
+ case VK_END: // 1
+ case VK_DOWN: // 2
+ case VK_NEXT: // 3
+ case VK_LEFT: // 4
+ case VK_CLEAR: // 5
+ case VK_RIGHT: // 6
+ case VK_HOME: // 7
+ case VK_UP: // 8
+ case VK_PRIOR: // 9
+ return control_key_state | SHIFT_PRESSED;
+ }
+ }
+
+ return control_key_state;
+}
+
+static const char* _get_keypad_sequence(const DWORD control_key_state,
+ const char* const normal, const char* const shifted) {
+ if (_is_shift_pressed(control_key_state)) {
+ // Shift is pressed and NumLock is off
+ return shifted;
+ } else {
+ // Shift is not pressed and NumLock is off, or,
+ // Shift is pressed and NumLock is on, in which case we want the
+ // NumLock and Shift to neutralize each other, thus, we want the normal
+ // sequence.
+ return normal;
+ }
+ // If Shift is not pressed and NumLock is on, a different virtual key code
+ // is returned by Windows, which can be taken care of by a different case
+ // statement in _console_read().
+}
+
+// Write sequence to buf and return the number of bytes written.
+static size_t _get_modifier_sequence(char* const buf, const WORD vk,
+ DWORD control_key_state, const char* const normal) {
+ // Copy the base sequence into buf.
+ const size_t len = strlen(normal);
+ memcpy(buf, normal, len);
+
+ int code = 0;
+
+ control_key_state = _normalize_keypad_control_key_state(vk,
+ control_key_state);
+
+ if (_is_shift_pressed(control_key_state)) {
+ code |= 0x1;
+ }
+ if (_is_alt_pressed(control_key_state)) { // any alt key pressed
+ code |= 0x2;
+ }
+ if (_is_ctrl_pressed(control_key_state)) { // any control key pressed
+ code |= 0x4;
+ }
+ // If some modifier was held down, then we need to insert the modifier code
+ if (code != 0) {
+ if (len == 0) {
+ // Should be impossible because caller should pass a string of
+ // non-zero length.
+ return 0;
+ }
+ size_t index = len - 1;
+ const char lastChar = buf[index];
+ if (lastChar != '~') {
+ buf[index++] = '1';
+ }
+ buf[index++] = ';'; // modifier separator
+ // 2 = shift, 3 = alt, 4 = shift & alt, 5 = control,
+ // 6 = shift & control, 7 = alt & control, 8 = shift & alt & control
+ buf[index++] = '1' + code;
+ buf[index++] = lastChar; // move ~ (or other last char) to the end
+ return index;
+ }
+ return len;
+}
+
+// Write sequence to buf and return the number of bytes written.
+static size_t _get_modifier_keypad_sequence(char* const buf, const WORD vk,
+ const DWORD control_key_state, const char* const normal,
+ const char shifted) {
+ if (_is_shift_pressed(control_key_state)) {
+ // Shift is pressed and NumLock is off
+ if (shifted != '\0') {
+ buf[0] = shifted;
+ return sizeof(buf[0]);
+ } else {
+ return 0;
+ }
+ } else {
+ // Shift is not pressed and NumLock is off, or,
+ // Shift is pressed and NumLock is on, in which case we want the
+ // NumLock and Shift to neutralize each other, thus, we want the normal
+ // sequence.
+ return _get_modifier_sequence(buf, vk, control_key_state, normal);
+ }
+ // If Shift is not pressed and NumLock is on, a different virtual key code
+ // is returned by Windows, which can be taken care of by a different case
+ // statement in _console_read().
+}
+
+// The decimal key on the keypad produces a '.' for U.S. English and a ',' for
+// Standard German. Figure this out at runtime so we know what to output for
+// Shift-VK_DELETE.
+static char _get_decimal_char() {
+ return (char)MapVirtualKeyA(VK_DECIMAL, MAPVK_VK_TO_CHAR);
+}
+
+// Prefix the len bytes in buf with the escape character, and then return the
+// new buffer length.
+size_t _escape_prefix(char* const buf, const size_t len) {
+ // If nothing to prefix, don't do anything. We might be called with
+ // len == 0, if alt was held down with a dead key which produced nothing.
+ if (len == 0) {
+ return 0;
+ }
+
+ memmove(&buf[1], buf, len);
+ buf[0] = '\x1b';
+ return len + 1;
+}
+
+// Writes to buffer buf (of length len), returning number of bytes written or
+// -1 on error. Never returns zero because Win32 consoles are never 'closed'
+// (as far as I can tell).
+static int _console_read(const HANDLE console, void* buf, size_t len) {
+ for (;;) {
+ KEY_EVENT_RECORD* const key_event = _get_key_event_record(console);
+ if (key_event == NULL) {
+ return -1;
+ }
+
+ const WORD vk = key_event->wVirtualKeyCode;
+ const CHAR ch = key_event->uChar.AsciiChar;
+ const DWORD control_key_state = _normalize_altgr_control_key_state(
+ key_event);
+
+ // The following emulation code should write the output sequence to
+ // either seqstr or to seqbuf and seqbuflen.
+ const char* seqstr = NULL; // NULL terminated C-string
+ // Enough space for max sequence string below, plus modifiers and/or
+ // escape prefix.
+ char seqbuf[16];
+ size_t seqbuflen = 0; // Space used in seqbuf.
+
+#define MATCH(vk, normal) \
+ case (vk): \
+ { \
+ seqstr = (normal); \
+ } \
+ break;
+
+ // Modifier keys should affect the output sequence.
+#define MATCH_MODIFIER(vk, normal) \
+ case (vk): \
+ { \
+ seqbuflen = _get_modifier_sequence(seqbuf, (vk), \
+ control_key_state, (normal)); \
+ } \
+ break;
+
+ // The shift key should affect the output sequence.
+#define MATCH_KEYPAD(vk, normal, shifted) \
+ case (vk): \
+ { \
+ seqstr = _get_keypad_sequence(control_key_state, (normal), \
+ (shifted)); \
+ } \
+ break;
+
+ // The shift key and other modifier keys should affect the output
+ // sequence.
+#define MATCH_MODIFIER_KEYPAD(vk, normal, shifted) \
+ case (vk): \
+ { \
+ seqbuflen = _get_modifier_keypad_sequence(seqbuf, (vk), \
+ control_key_state, (normal), (shifted)); \
+ } \
+ break;
+
+#define ESC "\x1b"
+#define CSI ESC "["
+#define SS3 ESC "O"
+
+ // Only support normal mode, not application mode.
+
+ // Enhanced keys:
+ // * 6-pack: insert, delete, home, end, page up, page down
+ // * cursor keys: up, down, right, left
+ // * keypad: divide, enter
+ // * Undocumented: VK_PAUSE (Ctrl-NumLock), VK_SNAPSHOT,
+ // VK_CANCEL (Ctrl-Pause/Break), VK_NUMLOCK
+ if (_is_enhanced_key(control_key_state)) {
+ switch (vk) {
+ case VK_RETURN: // Enter key on keypad
+ if (_is_ctrl_pressed(control_key_state)) {
+ seqstr = "\n";
+ } else {
+ seqstr = "\r";
+ }
+ break;
+
+ MATCH_MODIFIER(VK_PRIOR, CSI "5~"); // Page Up
+ MATCH_MODIFIER(VK_NEXT, CSI "6~"); // Page Down
+
+ // gnome-terminal currently sends SS3 "F" and SS3 "H", but that
+ // will be fixed soon to match xterm which sends CSI "F" and
+ // CSI "H". https://bugzilla.redhat.com/show_bug.cgi?id=1119764
+ MATCH(VK_END, CSI "F");
+ MATCH(VK_HOME, CSI "H");
+
+ MATCH_MODIFIER(VK_LEFT, CSI "D");
+ MATCH_MODIFIER(VK_UP, CSI "A");
+ MATCH_MODIFIER(VK_RIGHT, CSI "C");
+ MATCH_MODIFIER(VK_DOWN, CSI "B");
+
+ MATCH_MODIFIER(VK_INSERT, CSI "2~");
+ MATCH_MODIFIER(VK_DELETE, CSI "3~");
+
+ MATCH(VK_DIVIDE, "/");
+ }
+ } else { // Non-enhanced keys:
+ switch (vk) {
+ case VK_BACK: // backspace
+ if (_is_alt_pressed(control_key_state)) {
+ seqstr = ESC "\x7f";
+ } else {
+ seqstr = "\x7f";
+ }
+ break;
+
+ case VK_TAB:
+ if (_is_shift_pressed(control_key_state)) {
+ seqstr = CSI "Z";
+ } else {
+ seqstr = "\t";
+ }
+ break;
+
+ // Number 5 key in keypad when NumLock is off, or if NumLock is
+ // on and Shift is down.
+ MATCH_KEYPAD(VK_CLEAR, CSI "E", "5");
+
+ case VK_RETURN: // Enter key on main keyboard
+ if (_is_alt_pressed(control_key_state)) {
+ seqstr = ESC "\n";
+ } else if (_is_ctrl_pressed(control_key_state)) {
+ seqstr = "\n";
+ } else {
+ seqstr = "\r";
+ }
+ break;
+
+ // VK_ESCAPE: Don't do any special handling. The OS uses many
+ // of the sequences with Escape and many of the remaining
+ // sequences don't produce bKeyDown messages, only !bKeyDown
+ // for whatever reason.
+
+ case VK_SPACE:
+ if (_is_alt_pressed(control_key_state)) {
+ seqstr = ESC " ";
+ } else if (_is_ctrl_pressed(control_key_state)) {
+ seqbuf[0] = '\0'; // NULL char
+ seqbuflen = 1;
+ } else {
+ seqstr = " ";
+ }
+ break;
+
+ MATCH_MODIFIER_KEYPAD(VK_PRIOR, CSI "5~", '9'); // Page Up
+ MATCH_MODIFIER_KEYPAD(VK_NEXT, CSI "6~", '3'); // Page Down
+
+ MATCH_KEYPAD(VK_END, CSI "4~", "1");
+ MATCH_KEYPAD(VK_HOME, CSI "1~", "7");
+
+ MATCH_MODIFIER_KEYPAD(VK_LEFT, CSI "D", '4');
+ MATCH_MODIFIER_KEYPAD(VK_UP, CSI "A", '8');
+ MATCH_MODIFIER_KEYPAD(VK_RIGHT, CSI "C", '6');
+ MATCH_MODIFIER_KEYPAD(VK_DOWN, CSI "B", '2');
+
+ MATCH_MODIFIER_KEYPAD(VK_INSERT, CSI "2~", '0');
+ MATCH_MODIFIER_KEYPAD(VK_DELETE, CSI "3~",
+ _get_decimal_char());
+
+ case 0x30: // 0
+ case 0x31: // 1
+ case 0x39: // 9
+ case VK_OEM_1: // ;:
+ case VK_OEM_PLUS: // =+
+ case VK_OEM_COMMA: // ,<
+ case VK_OEM_PERIOD: // .>
+ case VK_OEM_7: // '"
+ case VK_OEM_102: // depends on keyboard, could be <> or \|
+ case VK_OEM_2: // /?
+ case VK_OEM_3: // `~
+ case VK_OEM_4: // [{
+ case VK_OEM_5: // \|
+ case VK_OEM_6: // ]}
+ {
+ seqbuflen = _get_control_character(seqbuf, key_event,
+ control_key_state);
+
+ if (_is_alt_pressed(control_key_state)) {
+ seqbuflen = _escape_prefix(seqbuf, seqbuflen);
+ }
+ }
+ break;
+
+ case 0x32: // 2
+ case 0x36: // 6
+ case VK_OEM_MINUS: // -_
+ {
+ seqbuflen = _get_control_character(seqbuf, key_event,
+ control_key_state);
+
+ // If Alt is pressed and it isn't Ctrl-Alt-ShiftUp, then
+ // prefix with escape.
+ if (_is_alt_pressed(control_key_state) &&
+ !(_is_ctrl_pressed(control_key_state) &&
+ !_is_shift_pressed(control_key_state))) {
+ seqbuflen = _escape_prefix(seqbuf, seqbuflen);
+ }
+ }
+ break;
+
+ case 0x33: // 3
+ case 0x34: // 4
+ case 0x35: // 5
+ case 0x37: // 7
+ case 0x38: // 8
+ {
+ seqbuflen = _get_control_character(seqbuf, key_event,
+ control_key_state);
+
+ // If Alt is pressed and it isn't Ctrl-Alt-ShiftUp, then
+ // prefix with escape.
+ if (_is_alt_pressed(control_key_state) &&
+ !(_is_ctrl_pressed(control_key_state) &&
+ !_is_shift_pressed(control_key_state))) {
+ seqbuflen = _escape_prefix(seqbuf, seqbuflen);
+ }
+ }
+ break;
+
+ case 0x41: // a
+ case 0x42: // b
+ case 0x43: // c
+ case 0x44: // d
+ case 0x45: // e
+ case 0x46: // f
+ case 0x47: // g
+ case 0x48: // h
+ case 0x49: // i
+ case 0x4a: // j
+ case 0x4b: // k
+ case 0x4c: // l
+ case 0x4d: // m
+ case 0x4e: // n
+ case 0x4f: // o
+ case 0x50: // p
+ case 0x51: // q
+ case 0x52: // r
+ case 0x53: // s
+ case 0x54: // t
+ case 0x55: // u
+ case 0x56: // v
+ case 0x57: // w
+ case 0x58: // x
+ case 0x59: // y
+ case 0x5a: // z
+ {
+ seqbuflen = _get_non_alt_char(seqbuf, key_event,
+ control_key_state);
+
+ // If Alt is pressed, then prefix with escape.
+ if (_is_alt_pressed(control_key_state)) {
+ seqbuflen = _escape_prefix(seqbuf, seqbuflen);
+ }
+ }
+ break;
+
+ // These virtual key codes are generated by the keys on the
+ // keypad *when NumLock is on* and *Shift is up*.
+ MATCH(VK_NUMPAD0, "0");
+ MATCH(VK_NUMPAD1, "1");
+ MATCH(VK_NUMPAD2, "2");
+ MATCH(VK_NUMPAD3, "3");
+ MATCH(VK_NUMPAD4, "4");
+ MATCH(VK_NUMPAD5, "5");
+ MATCH(VK_NUMPAD6, "6");
+ MATCH(VK_NUMPAD7, "7");
+ MATCH(VK_NUMPAD8, "8");
+ MATCH(VK_NUMPAD9, "9");
+
+ MATCH(VK_MULTIPLY, "*");
+ MATCH(VK_ADD, "+");
+ MATCH(VK_SUBTRACT, "-");
+ // VK_DECIMAL is generated by the . key on the keypad *when
+ // NumLock is on* and *Shift is up* and the sequence is not
+ // Ctrl-Alt-NoShift-. (which causes Ctrl-Alt-Del and the
+ // Windows Security screen to come up).
+ case VK_DECIMAL:
+ // U.S. English uses '.', Germany German uses ','.
+ seqbuflen = _get_non_control_char(seqbuf, key_event,
+ control_key_state);
+ break;
+
+ MATCH_MODIFIER(VK_F1, SS3 "P");
+ MATCH_MODIFIER(VK_F2, SS3 "Q");
+ MATCH_MODIFIER(VK_F3, SS3 "R");
+ MATCH_MODIFIER(VK_F4, SS3 "S");
+ MATCH_MODIFIER(VK_F5, CSI "15~");
+ MATCH_MODIFIER(VK_F6, CSI "17~");
+ MATCH_MODIFIER(VK_F7, CSI "18~");
+ MATCH_MODIFIER(VK_F8, CSI "19~");
+ MATCH_MODIFIER(VK_F9, CSI "20~");
+ MATCH_MODIFIER(VK_F10, CSI "21~");
+ MATCH_MODIFIER(VK_F11, CSI "23~");
+ MATCH_MODIFIER(VK_F12, CSI "24~");
+
+ MATCH_MODIFIER(VK_F13, CSI "25~");
+ MATCH_MODIFIER(VK_F14, CSI "26~");
+ MATCH_MODIFIER(VK_F15, CSI "28~");
+ MATCH_MODIFIER(VK_F16, CSI "29~");
+ MATCH_MODIFIER(VK_F17, CSI "31~");
+ MATCH_MODIFIER(VK_F18, CSI "32~");
+ MATCH_MODIFIER(VK_F19, CSI "33~");
+ MATCH_MODIFIER(VK_F20, CSI "34~");
+
+ // MATCH_MODIFIER(VK_F21, ???);
+ // MATCH_MODIFIER(VK_F22, ???);
+ // MATCH_MODIFIER(VK_F23, ???);
+ // MATCH_MODIFIER(VK_F24, ???);
+ }
+ }
+
+#undef MATCH
+#undef MATCH_MODIFIER
+#undef MATCH_KEYPAD
+#undef MATCH_MODIFIER_KEYPAD
+#undef ESC
+#undef CSI
+#undef SS3
+
+ const char* out;
+ size_t outlen;
+
+ // Check for output in any of:
+ // * seqstr is set (and strlen can be used to determine the length).
+ // * seqbuf and seqbuflen are set
+ // Fallback to ch from Windows.
+ if (seqstr != NULL) {
+ out = seqstr;
+ outlen = strlen(seqstr);
+ } else if (seqbuflen > 0) {
+ out = seqbuf;
+ outlen = seqbuflen;
+ } else if (ch != '\0') {
+ // Use whatever Windows told us it is.
+ seqbuf[0] = ch;
+ seqbuflen = 1;
+ out = seqbuf;
+ outlen = seqbuflen;
+ } else {
+ // No special handling for the virtual key code and Windows isn't
+ // telling us a character code, then we don't know how to translate
+ // the key press.
+ //
+ // Consume the input and 'continue' to cause us to get a new key
+ // event.
+ D("_console_read: unknown virtual key code: %d, enhanced: %s\n",
+ vk, _is_enhanced_key(control_key_state) ? "true" : "false");
+ key_event->wRepeatCount = 0;
+ continue;
+ }
+
+ int bytesRead = 0;
+
+ // put output wRepeatCount times into buf/len
+ while (key_event->wRepeatCount > 0) {
+ if (len >= outlen) {
+ // Write to buf/len
+ memcpy(buf, out, outlen);
+ buf = (void*)((char*)buf + outlen);
+ len -= outlen;
+ bytesRead += outlen;
+
+ // consume the input
+ --key_event->wRepeatCount;
+ } else {
+ // Not enough space, so just leave it in _win32_input_record
+ // for a subsequent retrieval.
+ if (bytesRead == 0) {
+ // We didn't write anything because there wasn't enough
+ // space to even write one sequence. This should never
+ // happen if the caller uses sensible buffer sizes
+ // (i.e. >= maximum sequence length which is probably a
+ // few bytes long).
+ D("_console_read: no buffer space to write one sequence; "
+ "buffer: %ld, sequence: %ld\n", (long)len,
+ (long)outlen);
+ errno = ENOMEM;
+ return -1;
+ } else {
+ // Stop trying to write to buf/len, just return whatever
+ // we wrote so far.
+ break;
+ }
+ }
+ }
+
+ return bytesRead;
+ }
+}
+
+static DWORD _old_console_mode; // previous GetConsoleMode() result
+static HANDLE _console_handle; // when set, console mode should be restored
+
+void stdin_raw_init(const int fd) {
+ if (STDIN_FILENO == fd) {
+ const HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
+ if ((in == INVALID_HANDLE_VALUE) || (in == NULL)) {
+ return;
+ }
+
+ if (GetFileType(in) != FILE_TYPE_CHAR) {
+ // stdin might be a file or pipe.
+ return;
+ }
+
+ if (!GetConsoleMode(in, &_old_console_mode)) {
+ // If GetConsoleMode() fails, stdin is probably is not a console.
+ return;
+ }
+
+ // Disable ENABLE_PROCESSED_INPUT so that Ctrl-C is read instead of
+ // calling the process Ctrl-C routine (configured by
+ // SetConsoleCtrlHandler()).
+ // Disable ENABLE_LINE_INPUT so that input is immediately sent.
+ // Disable ENABLE_ECHO_INPUT to disable local echo. Disabling this
+ // flag also seems necessary to have proper line-ending processing.
+ if (!SetConsoleMode(in, _old_console_mode & ~(ENABLE_PROCESSED_INPUT |
+ ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT))) {
+ // This really should not fail.
+ D("stdin_raw_init: SetConsoleMode() failure, error %ld\n",
+ GetLastError());
+ }
+
+ // Once this is set, it means that stdin has been configured for
+ // reading from and that the old console mode should be restored later.
+ _console_handle = in;
+
+ // Note that we don't need to configure C Runtime line-ending
+ // translation because _console_read() does not call the C Runtime to
+ // read from the console.
+ }
+}
+
+void stdin_raw_restore(const int fd) {
+ if (STDIN_FILENO == fd) {
+ if (_console_handle != NULL) {
+ const HANDLE in = _console_handle;
+ _console_handle = NULL; // clear state
+
+ if (!SetConsoleMode(in, _old_console_mode)) {
+ // This really should not fail.
+ D("stdin_raw_restore: SetConsoleMode() failure, error %ld\n",
+ GetLastError());
+ }
+ }
+ }
+}
+
+// Called by 'adb shell' command to read from stdin.
+int unix_read(int fd, void* buf, size_t len) {
+ if ((fd == STDIN_FILENO) && (_console_handle != NULL)) {
+ // If it is a request to read from stdin, and stdin_raw_init() has been
+ // called, and it successfully configured the console, then read from
+ // the console using Win32 console APIs and partially emulate a unix
+ // terminal.
+ return _console_read(_console_handle, buf, len);
+ } else {
+ // Just call into C Runtime which can read from pipes/files and which
+ // can do LF/CR translation.
+#undef read
+ return read(fd, buf, len);
+ }
+}
diff --git a/adb/test_track_devices.cpp b/adb/test_track_devices.cpp
index 77b3ad9..f78daeb 100644
--- a/adb/test_track_devices.cpp
+++ b/adb/test_track_devices.cpp
@@ -1,3 +1,5 @@
+// TODO: replace this with a shell/python script.
+
/* a simple test program, connects to ADB server, and opens a track-devices session */
#include <netdb.h>
#include <sys/socket.h>
@@ -6,6 +8,8 @@
#include <errno.h>
#include <memory.h>
+#include <base/file.h>
+
static void
panic( const char* msg )
{
@@ -13,85 +17,52 @@
exit(1);
}
-static int
-unix_write( int fd, const char* buf, int len )
-{
- int result = 0;
- while (len > 0) {
- int len2 = write(fd, buf, len);
- if (len2 < 0) {
- if (errno == EINTR || errno == EAGAIN)
- continue;
- return -1;
- }
- result += len2;
- len -= len2;
- buf += len2;
+int main(int argc, char* argv[]) {
+ const char* request = "host:track-devices";
+
+ if (argv[1] && strcmp(argv[1], "--jdwp") == 0) {
+ request = "track-jdwp";
}
- return result;
-}
-static int
-unix_read( int fd, char* buf, int len )
-{
- int result = 0;
- while (len > 0) {
- int len2 = read(fd, buf, len);
- if (len2 < 0) {
- if (errno == EINTR || errno == EAGAIN)
- continue;
- return -1;
- }
- result += len2;
- len -= len2;
- buf += len2;
- }
- return result;
-}
-
-
-int main( void )
-{
- int ret, s;
+ int ret;
struct sockaddr_in server;
char buffer[1024];
- const char* request = "host:track-devices";
- int len;
memset( &server, 0, sizeof(server) );
server.sin_family = AF_INET;
server.sin_port = htons(5037);
server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- s = socket( PF_INET, SOCK_STREAM, 0 );
+ int s = socket( PF_INET, SOCK_STREAM, 0 );
ret = connect( s, (struct sockaddr*) &server, sizeof(server) );
if (ret < 0) panic( "could not connect to server" );
/* send the request */
- len = snprintf( buffer, sizeof buffer, "%04x%s", strlen(request), request );
- if (unix_write(s, buffer, len) < 0)
+ int len = snprintf(buffer, sizeof(buffer), "%04zx%s", strlen(request), request);
+ if (!android::base::WriteFully(s, buffer, len))
panic( "could not send request" );
/* read the OKAY answer */
- if (unix_read(s, buffer, 4) != 4)
+ if (!android::base::ReadFully(s, buffer, 4))
panic( "could not read request" );
printf( "server answer: %.*s\n", 4, buffer );
/* now loop */
- for (;;) {
+ while (true) {
char head[5] = "0000";
- if (unix_read(s, head, 4) < 0)
+ if (!android::base::ReadFully(s, head, 4))
panic("could not read length");
- if ( sscanf( head, "%04x", &len ) != 1 )
+ int len;
+ if (sscanf(head, "%04x", &len) != 1 )
panic("could not decode length");
- if (unix_read(s, buffer, len) != len)
+ if (!android::base::ReadFully(s, buffer, len))
panic("could not read data");
- printf( "received header %.*s (%d bytes):\n%.*s", 4, head, len, len, buffer );
+ printf( "received header %.*s (%d bytes):\n%.*s----\n", 4, head, len, len, buffer );
}
close(s);
}
diff --git a/adb/test_track_jdwp.cpp b/adb/test_track_jdwp.cpp
deleted file mode 100644
index 8ecc6b8..0000000
--- a/adb/test_track_jdwp.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-/* a simple test program, connects to ADB server, and opens a track-devices session */
-#include <netdb.h>
-#include <sys/socket.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <memory.h>
-
-static void
-panic( const char* msg )
-{
- fprintf(stderr, "PANIC: %s: %s\n", msg, strerror(errno));
- exit(1);
-}
-
-static int
-unix_write( int fd, const char* buf, int len )
-{
- int result = 0;
- while (len > 0) {
- int len2 = write(fd, buf, len);
- if (len2 < 0) {
- if (errno == EINTR || errno == EAGAIN)
- continue;
- return -1;
- }
- result += len2;
- len -= len2;
- buf += len2;
- }
- return result;
-}
-
-static int
-unix_read( int fd, char* buf, int len )
-{
- int result = 0;
- while (len > 0) {
- int len2 = read(fd, buf, len);
- if (len2 < 0) {
- if (errno == EINTR || errno == EAGAIN)
- continue;
- return -1;
- }
- result += len2;
- len -= len2;
- buf += len2;
- }
- return result;
-}
-
-
-int main( void )
-{
- int ret, s;
- struct sockaddr_in server;
- char buffer[1024];
- const char* request = "track-jdwp";
- int len;
-
- memset( &server, 0, sizeof(server) );
- server.sin_family = AF_INET;
- server.sin_port = htons(5037);
- server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-
- s = socket( PF_INET, SOCK_STREAM, 0 );
- ret = connect( s, (struct sockaddr*) &server, sizeof(server) );
- if (ret < 0) panic( "could not connect to server" );
-
- /* send the request */
- len = snprintf( buffer, sizeof buffer, "%04x%s", strlen(request), request );
- if (unix_write(s, buffer, len) < 0)
- panic( "could not send request" );
-
- /* read the OKAY answer */
- if (unix_read(s, buffer, 4) != 4)
- panic( "could not read request" );
-
- printf( "server answer: %.*s\n", 4, buffer );
-
- /* now loop */
- for (;;) {
- char head[5] = "0000";
-
- if (unix_read(s, head, 4) < 0)
- panic("could not read length");
-
- if ( sscanf( head, "%04x", &len ) != 1 )
- panic("could not decode length");
-
- if (unix_read(s, buffer, len) != len)
- panic("could not read data");
-
- printf( "received header %.*s (%d bytes):\n%.*s", 4, head, len, len, buffer );
- }
- close(s);
-}
diff --git a/adb/tests/test_adb.py b/adb/tests/test_adb.py
index f111b043..237ef47 100755
--- a/adb/tests/test_adb.py
+++ b/adb/tests/test_adb.py
@@ -267,6 +267,29 @@
adb.unroot()
adb.wait()
+ def test_argument_escaping(self):
+ """Make sure that argument escaping is somewhat sane."""
+ adb = AdbWrapper()
+
+ # http://b/19734868
+ # Note that this actually matches ssh(1)'s behavior --- it's
+ # converted to "sh -c echo hello; echo world" which sh interprets
+ # as "sh -c echo" (with an argument to that shell of "hello"),
+ # and then "echo world" back in the first shell.
+ result = adb.shell("sh -c 'echo hello; echo world'").splitlines()
+ self.assertEqual(["", "world"], result)
+ # If you really wanted "hello" and "world", here's what you'd do:
+ result = adb.shell("echo hello\;echo world").splitlines()
+ self.assertEqual(["hello", "world"], result)
+
+ # http://b/15479704
+ self.assertEqual('t', adb.shell("'true && echo t'").strip())
+ self.assertEqual('t', adb.shell("sh -c 'true && echo t'").strip())
+
+ # http://b/20564385
+ self.assertEqual('t', adb.shell("FOO=a BAR=b echo t").strip())
+ self.assertEqual('123Linux', adb.shell("echo -n 123\;uname").strip())
+
class AdbFile(unittest.TestCase):
SCRATCH_DIR = "/data/local/tmp"
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 1f8ac03..29cf580 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-#include "sysdeps.h"
+#define TRACE_TAG TRACE_TRANSPORT
+#include "sysdeps.h"
#include "transport.h"
#include <ctype.h>
@@ -25,8 +26,10 @@
#include <string.h>
#include <unistd.h>
-#define TRACE_TAG TRACE_TRANSPORT
+#include <base/stringprintf.h>
+
#include "adb.h"
+#include "adb_utils.h"
static void transport_unref(atransport *t);
@@ -42,34 +45,6 @@
ADB_MUTEX_DEFINE( transport_lock );
-#if ADB_TRACE
-#define MAX_DUMP_HEX_LEN 16
-void dump_hex(const unsigned char* ptr, size_t len)
-{
- int nn, len2 = len;
- // Build a string instead of logging each character.
- // MAX chars in 2 digit hex, one space, MAX chars, one '\0'.
- char buffer[MAX_DUMP_HEX_LEN *2 + 1 + MAX_DUMP_HEX_LEN + 1 ], *pb = buffer;
-
- if (len2 > MAX_DUMP_HEX_LEN) len2 = MAX_DUMP_HEX_LEN;
-
- for (nn = 0; nn < len2; nn++) {
- sprintf(pb, "%02x", ptr[nn]);
- pb += 2;
- }
- sprintf(pb++, " ");
-
- for (nn = 0; nn < len2; nn++) {
- int c = ptr[nn];
- if (c < 32 || c > 127)
- c = '.';
- *pb++ = c;
- }
- *pb++ = '\0';
- DR("%s\n", buffer);
-}
-#endif
-
void kick_transport(atransport* t)
{
if (t && !t->kicked)
@@ -117,10 +92,7 @@
}
}
-#if ADB_TRACE
-static void
-dump_packet(const char* name, const char* func, apacket* p)
-{
+static void dump_packet(const char* name, const char* func, apacket* p) {
unsigned command = p->msg.command;
int len = p->msg.data_length;
char cmd[9];
@@ -155,7 +127,6 @@
name, func, cmd, arg0, arg1, len);
dump_hex(p->data, len);
}
-#endif /* ADB_TRACE */
static int
read_packet(int fd, const char* name, apacket** ppacket)
@@ -180,11 +151,9 @@
}
}
-#if ADB_TRACE
if (ADB_TRACING) {
dump_packet(name, "from remote", *ppacket);
}
-#endif
return 0;
}
@@ -199,11 +168,9 @@
name = buff;
}
-#if ADB_TRACE
if (ADB_TRACING) {
dump_packet(name, "to remote", *ppacket);
}
-#endif
len = sizeof(ppacket);
while(len > 0) {
r = adb_write(fd, p, len);
@@ -389,24 +356,12 @@
#if ADB_HOST
-static int list_transports_msg(char* buffer, size_t bufferlen)
-{
- char head[5];
- int len;
-
- len = list_transports(buffer+4, bufferlen-4, 0);
- snprintf(head, sizeof(head), "%04x", len);
- memcpy(buffer, head, 4);
- len += 4;
- return len;
-}
/* this adds support required by the 'track-devices' service.
* this is used to send the content of "list_transport" to any
* number of client connections that want it through a single
* live TCP connection
*/
-typedef struct device_tracker device_tracker;
struct device_tracker {
asocket socket;
int update_needed;
@@ -458,46 +413,34 @@
return -1;
}
-static int
-device_tracker_send( device_tracker* tracker,
- const char* buffer,
- int len )
-{
- apacket* p = get_apacket();
- asocket* peer = tracker->socket.peer;
+static int device_tracker_send(device_tracker* tracker, const std::string& string) {
+ apacket* p = get_apacket();
+ asocket* peer = tracker->socket.peer;
- memcpy(p->data, buffer, len);
- p->len = len;
- return peer->enqueue( peer, p );
+ snprintf(reinterpret_cast<char*>(p->data), 5, "%04x", static_cast<int>(string.size()));
+ memcpy(&p->data[4], string.data(), string.size());
+ p->len = 4 + string.size();
+ return peer->enqueue(peer, p);
}
+static void device_tracker_ready(asocket* socket) {
+ device_tracker* tracker = reinterpret_cast<device_tracker*>(socket);
-static void
-device_tracker_ready( asocket* socket )
-{
- device_tracker* tracker = (device_tracker*) socket;
-
- /* we want to send the device list when the tracker connects
- * for the first time, even if no update occured */
+ // We want to send the device list when the tracker connects
+ // for the first time, even if no update occurred.
if (tracker->update_needed > 0) {
- char buffer[1024];
- int len;
-
tracker->update_needed = 0;
- len = list_transports_msg(buffer, sizeof(buffer));
- device_tracker_send(tracker, buffer, len);
+ std::string transports = list_transports(false);
+ device_tracker_send(tracker, transports);
}
}
-
asocket*
create_device_tracker(void)
{
- device_tracker* tracker = reinterpret_cast<device_tracker*>(
- calloc(1, sizeof(*tracker)));
-
- if(tracker == 0) fatal("cannot allocate device tracker");
+ device_tracker* tracker = reinterpret_cast<device_tracker*>(calloc(1, sizeof(*tracker)));
+ if (tracker == nullptr) fatal("cannot allocate device tracker");
D( "device tracker %p created\n", tracker);
@@ -513,30 +456,27 @@
}
-/* call this function each time the transport list has changed */
-void update_transports(void) {
- char buffer[1024];
- int len;
- device_tracker* tracker;
+// Call this function each time the transport list has changed.
+void update_transports() {
+ std::string transports = list_transports(false);
- len = list_transports_msg(buffer, sizeof(buffer));
-
- tracker = device_tracker_list;
- while (tracker != NULL) {
- device_tracker* next = tracker->next;
- /* note: this may destroy the tracker if the connection is closed */
- device_tracker_send(tracker, buffer, len);
+ device_tracker* tracker = device_tracker_list;
+ while (tracker != nullptr) {
+ device_tracker* next = tracker->next;
+ // This may destroy the tracker if the connection is closed.
+ device_tracker_send(tracker, transports);
tracker = next;
}
}
+
#else
-void update_transports(void)
-{
- // nothing to do on the device side
+
+void update_transports() {
+ // Nothing to do on the device side.
}
+
#endif // ADB_HOST
-typedef struct tmsg tmsg;
struct tmsg
{
atransport *transport;
@@ -590,8 +530,6 @@
static void transport_registration_func(int _fd, unsigned ev, void *data)
{
tmsg m;
- adb_thread_t output_thread_ptr;
- adb_thread_t input_thread_ptr;
int s[2];
atransport *t;
@@ -660,11 +598,11 @@
fdevent_set(&(t->transport_fde), FDE_READ);
- if(adb_thread_create(&input_thread_ptr, input_thread, t)){
+ if (!adb_thread_create(input_thread, t)) {
fatal_errno("cannot create input thread");
}
- if(adb_thread_create(&output_thread_ptr, output_thread, t)){
+ if (!adb_thread_create(output_thread, t)) {
fatal_errno("cannot create output thread");
}
}
@@ -772,7 +710,7 @@
}
static int qual_match(const char *to_test,
- const char *prefix, const char *qual, int sanitize_qual)
+ const char *prefix, const char *qual, bool sanitize_qual)
{
if (!to_test || !*to_test)
/* Return true if both the qual and to_test are null strings. */
@@ -790,7 +728,7 @@
while (*qual) {
char ch = *qual++;
- if (sanitize_qual && isalnum(ch))
+ if (sanitize_qual && !isalnum(ch))
ch = '_';
if (ch != *to_test++)
return 0;
@@ -800,22 +738,20 @@
return !*to_test;
}
-atransport *acquire_one_transport(int state, transport_type ttype,
- const char* serial, const char** error_out)
+atransport* acquire_one_transport(int state, TransportType type,
+ const char* serial, std::string* error_out)
{
atransport *t;
atransport *result = NULL;
int ambiguous = 0;
retry:
- if (error_out)
- *error_out = "device not found";
+ if (error_out) *error_out = android::base::StringPrintf("device '%s' not found", serial);
adb_mutex_lock(&transport_lock);
for (t = transport_list.next; t != &transport_list; t = t->next) {
if (t->connection_state == CS_NOPERM) {
- if (error_out)
- *error_out = "insufficient permissions for device";
+ if (error_out) *error_out = "insufficient permissions for device";
continue;
}
@@ -823,12 +759,11 @@
if (serial) {
if ((t->serial && !strcmp(serial, t->serial)) ||
(t->devpath && !strcmp(serial, t->devpath)) ||
- qual_match(serial, "product:", t->product, 0) ||
- qual_match(serial, "model:", t->model, 1) ||
- qual_match(serial, "device:", t->device, 0)) {
+ qual_match(serial, "product:", t->product, false) ||
+ qual_match(serial, "model:", t->model, true) ||
+ qual_match(serial, "device:", t->device, false)) {
if (result) {
- if (error_out)
- *error_out = "more than one device";
+ if (error_out) *error_out = "more than one device";
ambiguous = 1;
result = NULL;
break;
@@ -836,28 +771,25 @@
result = t;
}
} else {
- if (ttype == kTransportUsb && t->type == kTransportUsb) {
+ if (type == kTransportUsb && t->type == kTransportUsb) {
if (result) {
- if (error_out)
- *error_out = "more than one device";
+ if (error_out) *error_out = "more than one device";
ambiguous = 1;
result = NULL;
break;
}
result = t;
- } else if (ttype == kTransportLocal && t->type == kTransportLocal) {
+ } else if (type == kTransportLocal && t->type == kTransportLocal) {
if (result) {
- if (error_out)
- *error_out = "more than one emulator";
+ if (error_out) *error_out = "more than one emulator";
ambiguous = 1;
result = NULL;
break;
}
result = t;
- } else if (ttype == kTransportAny) {
+ } else if (type == kTransportAny) {
if (result) {
- if (error_out)
- *error_out = "more than one device and emulator";
+ if (error_out) *error_out = "more than one device/emulator";
ambiguous = 1;
result = NULL;
break;
@@ -870,29 +802,33 @@
if (result) {
if (result->connection_state == CS_UNAUTHORIZED) {
- if (error_out)
- *error_out = "device unauthorized. Please check the confirmation dialog on your device.";
+ if (error_out) {
+ *error_out = "device unauthorized.\n";
+ char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
+ *error_out += "This adbd's $ADB_VENDOR_KEYS is ";
+ *error_out += ADB_VENDOR_KEYS ? ADB_VENDOR_KEYS : "not set";
+ *error_out += "; try 'adb kill-server' if that seems wrong.\n";
+ *error_out += "Otherwise check for a confirmation dialog on your device.";
+ }
result = NULL;
}
- /* offline devices are ignored -- they are either being born or dying */
+ /* offline devices are ignored -- they are either being born or dying */
if (result && result->connection_state == CS_OFFLINE) {
- if (error_out)
- *error_out = "device offline";
+ if (error_out) *error_out = "device offline";
result = NULL;
}
- /* check for required connection state */
+
+ /* check for required connection state */
if (result && state != CS_ANY && result->connection_state != state) {
- if (error_out)
- *error_out = "invalid device state";
+ if (error_out) *error_out = "invalid device state";
result = NULL;
}
}
if (result) {
/* found one that we can take */
- if (error_out)
- *error_out = NULL;
+ if (error_out) *error_out = "success";
} else if (state != CS_ANY && (serial || !ambiguous)) {
adb_sleep_ms(1000);
goto retry;
@@ -901,103 +837,72 @@
return result;
}
-#if ADB_HOST
-static const char *statename(atransport *t)
-{
- switch(t->connection_state){
+const char* atransport::connection_state_name() const {
+ switch (connection_state) {
case CS_OFFLINE: return "offline";
case CS_BOOTLOADER: return "bootloader";
case CS_DEVICE: return "device";
case CS_HOST: return "host";
case CS_RECOVERY: return "recovery";
- case CS_SIDELOAD: return "sideload";
case CS_NOPERM: return "no permissions";
+ case CS_SIDELOAD: return "sideload";
case CS_UNAUTHORIZED: return "unauthorized";
default: return "unknown";
}
}
-static void add_qual(char **buf, size_t *buf_size,
- const char *prefix, const char *qual, int sanitize_qual)
-{
- size_t len;
- int prefix_len;
+#if ADB_HOST
- if (!buf || !*buf || !buf_size || !*buf_size || !qual || !*qual)
+static void append_transport_info(std::string* result, const char* key,
+ const char* value, bool sanitize) {
+ if (value == nullptr || *value == '\0') {
return;
-
- len = snprintf(*buf, *buf_size, "%s%n%s", prefix, &prefix_len, qual);
-
- if (sanitize_qual) {
- char *cp;
- for (cp = *buf + prefix_len; cp < *buf + len; cp++) {
- if (isalnum(*cp))
- *cp = '_';
- }
}
- *buf_size -= len;
- *buf += len;
+ *result += ' ';
+ *result += key;
+
+ for (const char* p = value; *p; ++p) {
+ result->push_back((!sanitize || isalnum(*p)) ? *p : '_');
+ }
}
-static size_t format_transport(atransport *t, char *buf, size_t bufsize,
- int long_listing)
-{
+static void append_transport(atransport* t, std::string* result, bool long_listing) {
const char* serial = t->serial;
- if (!serial || !serial[0])
- serial = "????????????";
+ if (!serial || !serial[0]) {
+ serial = "(no serial number)";
+ }
if (!long_listing) {
- return snprintf(buf, bufsize, "%s\t%s\n", serial, statename(t));
+ *result += serial;
+ *result += '\t';
+ *result += t->connection_state_name();
} else {
- size_t len, remaining = bufsize;
+ android::base::StringAppendF(result, "%-22s %s", serial, t->connection_state_name());
- len = snprintf(buf, remaining, "%-22s %s", serial, statename(t));
- remaining -= len;
- buf += len;
-
- add_qual(&buf, &remaining, " ", t->devpath, 0);
- add_qual(&buf, &remaining, " product:", t->product, 0);
- add_qual(&buf, &remaining, " model:", t->model, 1);
- add_qual(&buf, &remaining, " device:", t->device, 0);
-
- len = snprintf(buf, remaining, "\n");
- remaining -= len;
-
- return bufsize - remaining;
+ append_transport_info(result, "", t->devpath, false);
+ append_transport_info(result, "product:", t->product, false);
+ append_transport_info(result, "model:", t->model, true);
+ append_transport_info(result, "device:", t->device, false);
}
+ *result += '\n';
}
-int list_transports(char *buf, size_t bufsize, int long_listing)
-{
- char* p = buf;
- char* end = buf + bufsize;
- int len;
- atransport *t;
-
- /* XXX OVERRUN PROBLEMS XXX */
+std::string list_transports(bool long_listing) {
+ std::string result;
adb_mutex_lock(&transport_lock);
- for(t = transport_list.next; t != &transport_list; t = t->next) {
- len = format_transport(t, p, end - p, long_listing);
- if (p + len >= end) {
- /* discard last line if buffer is too short */
- break;
- }
- p += len;
+ for (atransport* t = transport_list.next; t != &transport_list; t = t->next) {
+ append_transport(t, &result, long_listing);
}
- p[0] = 0;
adb_mutex_unlock(&transport_lock);
- return p - buf;
+ return result;
}
-
/* hack for osx */
void close_usb_devices()
{
- atransport *t;
-
adb_mutex_lock(&transport_lock);
- for(t = transport_list.next; t != &transport_list; t = t->next) {
+ for (atransport* t = transport_list.next; t != &transport_list; t = t->next) {
if ( !t->kicked ) {
t->kicked = 1;
t->kick(t);
@@ -1009,8 +914,11 @@
int register_socket_transport(int s, const char *serial, int port, int local)
{
- atransport *t = reinterpret_cast<atransport*>(
- calloc(1, sizeof(atransport)));
+ atransport *t = reinterpret_cast<atransport*>(calloc(1, sizeof(atransport)));
+ if (t == nullptr) {
+ return -1;
+ }
+
atransport *n;
char buff[32];
@@ -1109,8 +1017,8 @@
void register_usb_transport(usb_handle *usb, const char *serial, const char *devpath, unsigned writeable)
{
- atransport *t = reinterpret_cast<atransport*>(
- calloc(1, sizeof(atransport)));
+ atransport *t = reinterpret_cast<atransport*>(calloc(1, sizeof(atransport)));
+ if (t == nullptr) fatal("cannot allocate USB atransport");
D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb,
serial ? serial : "");
init_usb_transport(t, usb, (writeable ? CS_OFFLINE : CS_NOPERM));
diff --git a/adb/transport.h b/adb/transport.h
index 36a0e40..7b799b9 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -17,27 +17,20 @@
#ifndef __TRANSPORT_H
#define __TRANSPORT_H
-#include <stdbool.h>
#include <sys/types.h>
+#include <string>
+
#include "adb.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#if ADB_TRACE
-void dump_hex(const unsigned char* ptr, size_t len);
-#endif
-
/*
* Obtain a transport from the available transports.
* If state is != CS_ANY, 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.
*/
-atransport* acquire_one_transport(int state, transport_type ttype,
- const char* serial, const char** error_out);
+atransport* acquire_one_transport(int state, TransportType type,
+ const char* serial, std::string* error_out);
void add_transport_disconnect(atransport* t, adisconnect* dis);
void remove_transport_disconnect(atransport* t, adisconnect* dis);
void kick_transport(atransport* t);
@@ -48,7 +41,7 @@
** get_device_transport does an acquire on your behalf before returning
*/
void init_transport_registration(void);
-int list_transports(char* buf, size_t bufsize, int long_listing);
+std::string list_transports(bool long_listing);
atransport* find_transport(const char* serial);
void register_usb_transport(usb_handle* h, const char* serial,
@@ -74,8 +67,4 @@
asocket* create_device_tracker(void);
-#ifdef __cplusplus
-}
-#endif
-
#endif /* __TRANSPORT_H */
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 440d4c5..5f7449d 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -14,21 +14,25 @@
* limitations under the License.
*/
+#define TRACE_TAG TRACE_TRANSPORT
+
+#include "sysdeps.h"
+#include "transport.h"
+
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
-#include "sysdeps.h"
+#include <base/stringprintf.h>
-#define TRACE_TAG TRACE_TRANSPORT
-#include "adb.h"
-#include "adb_io.h"
#if !ADB_HOST
#include "cutils/properties.h"
#endif
-#include "transport.h"
+
+#include "adb.h"
+#include "adb_io.h"
#if ADB_HOST
/* we keep a list of opened transports. The atransport struct knows to which
@@ -86,7 +90,6 @@
int local_connect_arbitrary_ports(int console_port, int adb_port)
{
- char buf[64];
int fd = -1;
#if ADB_HOST
@@ -103,8 +106,8 @@
D("client: connected on remote on fd %d\n", fd);
close_on_exec(fd);
disable_tcp_nagle(fd);
- snprintf(buf, sizeof buf, "%s%d", LOCAL_CLIENT_PREFIX, console_port);
- register_socket_transport(fd, buf, adb_port, 1);
+ std::string serial = android::base::StringPrintf("emulator-%d", console_port);
+ register_socket_transport(fd, serial.c_str(), adb_port, 1);
return 0;
}
return -1;
@@ -142,7 +145,7 @@
if(serverfd == -1) {
serverfd = socket_inaddr_any_server(port, SOCK_STREAM);
if(serverfd < 0) {
- D("server: cannot bind socket yet\n");
+ D("server: cannot bind socket yet: %s\n", strerror(errno));
adb_sleep_ms(1000);
continue;
}
@@ -231,9 +234,8 @@
if (fd < 0) {
/* This could be an older version of the emulator, that doesn't
* implement adb QEMUD service. Fall back to the old TCP way. */
- adb_thread_t thr;
D("adb service is not available. Falling back to TCP socket.\n");
- adb_thread_create(&thr, server_socket_thread, arg);
+ adb_thread_create(server_socket_thread, arg);
return 0;
}
@@ -276,7 +278,6 @@
void local_init(int port)
{
- adb_thread_t thr;
void* (*func)(void *);
if(HOST) {
@@ -301,9 +302,8 @@
D("transport: local %s init\n", HOST ? "client" : "server");
- if(adb_thread_create(&thr, func, (void *) (uintptr_t) port)) {
- fatal_errno("cannot create local socket %s thread",
- HOST ? "client" : "server");
+ if (!adb_thread_create(func, (void *) (uintptr_t) port)) {
+ fatal_errno("cannot create local socket %s thread", HOST ? "client" : "server");
}
}
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 37a8219..cdabffe 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -14,15 +14,16 @@
* limitations under the License.
*/
+#define TRACE_TAG TRACE_TRANSPORT
+
+#include "sysdeps.h"
+#include "transport.h"
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sysdeps.h>
-
-#define TRACE_TAG TRACE_TRANSPORT
#include "adb.h"
-#include "transport.h"
static int remote_read(apacket *p, atransport *t)
{
diff --git a/adb/usb_linux.cpp b/adb/usb_linux.cpp
index c01ec8c..1e5d5c8 100644
--- a/adb/usb_linux.cpp
+++ b/adb/usb_linux.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#define TRACE_TAG TRACE_USB
+
+#include "sysdeps.h"
+
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
@@ -27,15 +31,12 @@
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
#include <linux/usb/ch9.h>
-#else
-#include <linux/usb_ch9.h>
-#endif
-#include "sysdeps.h"
+#include <base/file.h>
+#include <base/stringprintf.h>
+#include <base/strings.h>
-#define TRACE_TAG TRACE_USB
#include "adb.h"
#include "transport.h"
@@ -113,10 +114,6 @@
}
-static void register_device(const char *dev_name, const char *devpath,
- unsigned char ep_in, unsigned char ep_out,
- int ifc, int serial_index, unsigned zero_mask);
-
static inline int badname(const char *name)
{
while(*name) {
@@ -313,10 +310,6 @@
closedir(busdir);
}
-void usb_cleanup()
-{
-}
-
static int usb_bulk_write(usb_handle *h, const void *data, int len)
{
struct usbdevfs_urb *urb = &h->urb_out;
@@ -570,24 +563,19 @@
return 0;
}
-static void register_device(const char *dev_name, const char *devpath,
+static void register_device(const char* dev_name, const char* dev_path,
unsigned char ep_in, unsigned char ep_out,
- int interface, int serial_index, unsigned zero_mask)
-{
- int n = 0;
- char serial[256];
-
- /* Since Linux will not reassign the device ID (and dev_name)
- ** as long as the device is open, we can add to the list here
- ** once we open it and remove from the list when we're finally
- ** closed and everything will work out fine.
- **
- ** If we have a usb_handle on the list 'o handles with a matching
- ** name, we have no further work to do.
- */
+ int interface, int serial_index,
+ unsigned zero_mask) {
+ // Since Linux will not reassign the device ID (and dev_name) as long as the
+ // device is open, we can add to the list here once we open it and remove
+ // from the list when we're finally closed and everything will work out
+ // fine.
+ //
+ // If we have a usb_handle on the list 'o handles with a matching name, we
+ // have no further work to do.
adb_mutex_lock(&usb_lock);
- for (usb_handle* usb = handle_list.next; usb != &handle_list;
- usb = usb->next) {
+ for (usb_handle* usb = handle_list.next; usb != &handle_list; usb = usb->next) {
if (!strcmp(usb->fname, dev_name)) {
adb_mutex_unlock(&usb_lock);
return;
@@ -595,10 +583,9 @@
}
adb_mutex_unlock(&usb_lock);
- D("[ usb located new device %s (%d/%d/%d) ]\n",
- dev_name, ep_in, ep_out, interface);
- usb_handle* usb = reinterpret_cast<usb_handle*>(
- calloc(1, sizeof(usb_handle)));
+ D("[ usb located new device %s (%d/%d/%d) ]\n", dev_name, ep_in, ep_out, interface);
+ usb_handle* usb = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
+ if (usb == nullptr) fatal("couldn't allocate usb_handle");
strcpy(usb->fname, dev_name);
usb->ep_in = ep_in;
usb->ep_out = ep_out;
@@ -607,75 +594,50 @@
adb_cond_init(&usb->notify, 0);
adb_mutex_init(&usb->lock, 0);
- /* initialize mark to 1 so we don't get garbage collected after the device scan */
+ // Initialize mark to 1 so we don't get garbage collected after the device
+ // scan.
usb->mark = 1;
usb->reaper_thread = 0;
usb->desc = unix_open(usb->fname, O_RDWR | O_CLOEXEC);
- if(usb->desc < 0) {
- /* if we fail, see if have read-only access */
+ if (usb->desc == -1) {
+ // Opening RW failed, so see if we have RO access.
usb->desc = unix_open(usb->fname, O_RDONLY | O_CLOEXEC);
- if(usb->desc < 0) goto fail;
+ if (usb->desc == -1) {
+ D("[ usb open %s failed: %s]\n", usb->fname, strerror(errno));
+ free(usb);
+ return;
+ }
usb->writeable = 0;
- D("[ usb open read-only %s fd = %d]\n", usb->fname, usb->desc);
- } else {
- D("[ usb open %s fd = %d]\n", usb->fname, usb->desc);
- n = ioctl(usb->desc, USBDEVFS_CLAIMINTERFACE, &interface);
- if(n != 0) goto fail;
}
- /* read the device's serial number */
- serial[0] = 0;
- memset(serial, 0, sizeof(serial));
- if (serial_index) {
- struct usbdevfs_ctrltransfer ctrl;
- __u16 buffer[128];
- __u16 languages[128];
- int i, result;
- int languageCount = 0;
+ D("[ usb opened %s%s, fd=%d]\n", usb->fname,
+ (usb->writeable ? "" : " (read-only)"), usb->desc);
- memset(languages, 0, sizeof(languages));
- memset(&ctrl, 0, sizeof(ctrl));
-
- // read list of supported languages
- ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
- ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
- ctrl.wValue = (USB_DT_STRING << 8) | 0;
- ctrl.wIndex = 0;
- ctrl.wLength = sizeof(languages);
- ctrl.data = languages;
- ctrl.timeout = 1000;
-
- result = ioctl(usb->desc, USBDEVFS_CONTROL, &ctrl);
- if (result > 0)
- languageCount = (result - 2) / 2;
-
- for (i = 1; i <= languageCount; i++) {
- memset(buffer, 0, sizeof(buffer));
- memset(&ctrl, 0, sizeof(ctrl));
-
- ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
- ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
- ctrl.wValue = (USB_DT_STRING << 8) | serial_index;
- ctrl.wIndex = __le16_to_cpu(languages[i]);
- ctrl.wLength = sizeof(buffer);
- ctrl.data = buffer;
- ctrl.timeout = 1000;
-
- result = ioctl(usb->desc, USBDEVFS_CONTROL, &ctrl);
- if (result > 0) {
- int i;
- // skip first word, and copy the rest to the serial string, changing shorts to bytes.
- result /= 2;
- for (i = 1; i < result; i++)
- serial[i - 1] = __le16_to_cpu(buffer[i]);
- serial[i - 1] = 0;
- break;
- }
+ if (usb->writeable) {
+ if (ioctl(usb->desc, USBDEVFS_CLAIMINTERFACE, &interface) != 0) {
+ D("[ usb ioctl(%d, USBDEVFS_CLAIMINTERFACE) failed: %s]\n",
+ usb->desc, strerror(errno));
+ adb_close(usb->desc);
+ free(usb);
+ return;
}
}
- /* add to the end of the active handles */
+ // Read the device's serial number.
+ std::string serial_path = android::base::StringPrintf(
+ "/sys/bus/usb/devices/%s/serial", dev_path + 4);
+ std::string serial;
+ if (!android::base::ReadFileToString(serial_path, &serial)) {
+ D("[ usb read %s failed: %s ]\n", serial_path.c_str(), strerror(errno));
+ // We don't actually want to treat an unknown serial as an error because
+ // devices aren't able to communicate a serial number in early bringup.
+ // http://b/20883914
+ serial = "";
+ }
+ serial = android::base::Trim(serial);
+
+ // Add to the end of the active handles.
adb_mutex_lock(&usb_lock);
usb->next = &handle_list;
usb->prev = handle_list.prev;
@@ -683,38 +645,26 @@
usb->next->prev = usb;
adb_mutex_unlock(&usb_lock);
- register_usb_transport(usb, serial, devpath, usb->writeable);
- return;
-
-fail:
- D("[ usb open %s error=%d, err_str = %s]\n",
- usb->fname, errno, strerror(errno));
- if(usb->desc >= 0) {
- adb_close(usb->desc);
- }
- free(usb);
+ register_usb_transport(usb, serial.c_str(), dev_path, usb->writeable);
}
-void* device_poll_thread(void* unused)
-{
+static void* device_poll_thread(void* unused) {
D("Created device thread\n");
- for(;;) {
- /* XXX use inotify */
+ while (true) {
+ // TODO: Use inotify.
find_usb_device("/dev/bus/usb", register_device);
kick_disconnected_devices();
sleep(1);
}
- return NULL;
+ return nullptr;
}
-static void sigalrm_handler(int signo)
-{
+static void sigalrm_handler(int signo) {
// don't need to do anything here
}
void usb_init()
{
- adb_thread_t tid;
struct sigaction actions;
memset(&actions, 0, sizeof(actions));
@@ -723,7 +673,7 @@
actions.sa_handler = sigalrm_handler;
sigaction(SIGALRM,& actions, NULL);
- if(adb_thread_create(&tid, device_poll_thread, NULL)){
+ if (!adb_thread_create(device_poll_thread, nullptr)) {
fatal_errno("cannot create input thread");
}
}
diff --git a/adb/usb_linux_client.c b/adb/usb_linux_client.cpp
similarity index 90%
rename from adb/usb_linux_client.c
rename to adb/usb_linux_client.cpp
index c88b258..63bb1c7 100644
--- a/adb/usb_linux_client.c
+++ b/adb/usb_linux_client.cpp
@@ -14,6 +14,11 @@
* limitations under the License.
*/
+#define TRACE_TAG TRACE_USB
+
+#include "sysdeps.h"
+
+#include <cutils/properties.h>
#include <dirent.h>
#include <errno.h>
#include <linux/usb/ch9.h>
@@ -25,9 +30,6 @@
#include <sys/types.h>
#include <unistd.h>
-#include "sysdeps.h"
-
-#define TRACE_TAG TRACE_USB
#include "adb.h"
#include "transport.h"
@@ -80,7 +82,7 @@
struct func_desc fs_descs, hs_descs;
} __attribute__((packed));
-struct func_desc fs_descriptors = {
+static struct func_desc fs_descriptors = {
.intf = {
.bLength = sizeof(fs_descriptors.intf),
.bDescriptorType = USB_DT_INTERFACE,
@@ -107,7 +109,7 @@
},
};
-struct func_desc hs_descriptors = {
+static struct func_desc hs_descriptors = {
.intf = {
.bLength = sizeof(hs_descriptors.intf),
.bDescriptorType = USB_DT_INTERFACE,
@@ -162,7 +164,7 @@
struct usb_handle *usb = (struct usb_handle *)x;
int fd;
- while (1) {
+ while (true) {
// wait until the USB device needs opening
adb_mutex_lock(&usb->lock);
while (usb->fd != -1)
@@ -238,11 +240,8 @@
static void usb_adb_init()
{
- usb_handle *h;
- adb_thread_t tid;
- int fd;
-
- h = calloc(1, sizeof(usb_handle));
+ usb_handle* h = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
+ if (h == nullptr) fatal("couldn't allocate usb_handle");
h->write = usb_adb_write;
h->read = usb_adb_read;
@@ -252,12 +251,12 @@
adb_cond_init(&h->notify, 0);
adb_mutex_init(&h->lock, 0);
- // Open the file /dev/android_adb_enable to trigger
+ // Open the file /dev/android_adb_enable to trigger
// the enabling of the adb USB function in the kernel.
// We never touch this file again - just leave it open
// indefinitely so the kernel will know when we are running
// and when we are not.
- fd = unix_open("/dev/android_adb_enable", O_RDWR);
+ int fd = unix_open("/dev/android_adb_enable", O_RDWR);
if (fd < 0) {
D("failed to open /dev/android_adb_enable\n");
} else {
@@ -265,7 +264,7 @@
}
D("[ usb_init - starting thread ]\n");
- if(adb_thread_create(&tid, usb_adb_open_thread, h)){
+ if (!adb_thread_create(usb_adb_open_thread, h)) {
fatal_errno("cannot create usb thread");
}
}
@@ -350,14 +349,14 @@
{
struct usb_handle *usb = (struct usb_handle *)x;
- while (1) {
+ while (true) {
// wait until the USB device needs opening
adb_mutex_lock(&usb->lock);
while (usb->control != -1 && usb->bulk_in != -1 && usb->bulk_out != -1)
adb_cond_wait(&usb->notify, &usb->lock);
adb_mutex_unlock(&usb->lock);
- while (1) {
+ while (true) {
init_functionfs(usb);
if (usb->control >= 0 && usb->bulk_in >= 0 && usb->bulk_out >= 0)
@@ -365,6 +364,7 @@
adb_sleep_ms(1000);
}
+ property_set("sys.usb.ffs.ready", "1");
D("[ usb_thread - registering device ]\n");
register_usb_transport(usb, 0, 0, 1);
@@ -374,7 +374,7 @@
return 0;
}
-static int bulk_write(int bulk_in, const char *buf, size_t length)
+static int bulk_write(int bulk_in, const uint8_t* buf, size_t length)
{
size_t count = 0;
int ret;
@@ -393,22 +393,19 @@
return count;
}
-static int usb_ffs_write(usb_handle *h, const void *data, int len)
+static int usb_ffs_write(usb_handle* h, const void* data, int len)
{
- int n;
-
D("about to write (fd=%d, len=%d)\n", h->bulk_in, len);
- n = bulk_write(h->bulk_in, data, len);
+ int n = bulk_write(h->bulk_in, reinterpret_cast<const uint8_t*>(data), len);
if (n != len) {
- D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
- h->bulk_in, n, errno, strerror(errno));
+ D("ERROR: fd = %d, n = %d: %s\n", h->bulk_in, n, strerror(errno));
return -1;
}
D("[ done fd=%d ]\n", h->bulk_in);
return 0;
}
-static int bulk_read(int bulk_out, char *buf, size_t length)
+static int bulk_read(int bulk_out, uint8_t* buf, size_t length)
{
size_t count = 0;
int ret;
@@ -429,15 +426,12 @@
return count;
}
-static int usb_ffs_read(usb_handle *h, void *data, int len)
+static int usb_ffs_read(usb_handle* h, void* data, int len)
{
- int n;
-
D("about to read (fd=%d, len=%d)\n", h->bulk_out, len);
- n = bulk_read(h->bulk_out, data, len);
+ int n = bulk_read(h->bulk_out, reinterpret_cast<uint8_t*>(data), len);
if (n != len) {
- D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
- h->bulk_out, n, errno, strerror(errno));
+ D("ERROR: fd = %d, n = %d: %s\n", h->bulk_out, n, strerror(errno));
return -1;
}
D("[ done fd=%d ]\n", h->bulk_out);
@@ -472,18 +466,15 @@
static void usb_ffs_init()
{
- usb_handle *h;
- adb_thread_t tid;
-
D("[ usb_init - using FunctionFS ]\n");
- h = calloc(1, sizeof(usb_handle));
+ usb_handle* h = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
+ if (h == nullptr) fatal("couldn't allocate usb_handle");
h->write = usb_ffs_write;
h->read = usb_ffs_read;
h->kick = usb_ffs_kick;
-
- h->control = -1;
+ h->control = -1;
h->bulk_out = -1;
h->bulk_out = -1;
@@ -491,7 +482,7 @@
adb_mutex_init(&h->lock, 0);
D("[ usb_init - starting thread ]\n");
- if (adb_thread_create(&tid, usb_ffs_open_thread, h)){
+ if (!adb_thread_create(usb_ffs_open_thread, h)) {
fatal_errno("[ cannot create usb thread ]\n");
}
}
@@ -504,10 +495,6 @@
usb_adb_init();
}
-void usb_cleanup()
-{
-}
-
int usb_write(usb_handle *h, const void *data, int len)
{
return h->write(h, data, len);
diff --git a/adb/usb_osx.c b/adb/usb_osx.cpp
similarity index 94%
rename from adb/usb_osx.c
rename to adb/usb_osx.cpp
index aa7e1ea..af65130 100644
--- a/adb/usb_osx.c
+++ b/adb/usb_osx.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#define TRACE_TAG TRACE_USB
+
+#include "sysdeps.h"
+
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
@@ -22,11 +26,9 @@
#include <IOKit/IOMessage.h>
#include <mach/mach_port.h>
+#include <inttypes.h>
#include <stdio.h>
-#include "sysdeps.h"
-
-#define TRACE_TAG TRACE_USB
#include "adb.h"
#include "transport.h"
@@ -110,7 +112,7 @@
HRESULT result;
SInt32 score;
UInt32 locationId;
- UInt8 class, subclass, protocol;
+ UInt8 if_class, subclass, protocol;
UInt16 vendor;
UInt16 product;
UInt8 serialIndex;
@@ -131,9 +133,9 @@
}
//* This gets us the interface object
- result = (*plugInInterface)->QueryInterface(plugInInterface,
- CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID)
- &iface);
+ result = (*plugInInterface)->QueryInterface(
+ plugInInterface,
+ CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID*)&iface);
//* We only needed the plugin to get the interface, so discard it
(*plugInInterface)->Release(plugInInterface);
if (result || !iface) {
@@ -141,12 +143,12 @@
continue;
}
- kr = (*iface)->GetInterfaceClass(iface, &class);
+ kr = (*iface)->GetInterfaceClass(iface, &if_class);
kr = (*iface)->GetInterfaceSubClass(iface, &subclass);
kr = (*iface)->GetInterfaceProtocol(iface, &protocol);
- if(class != ADB_CLASS || subclass != ADB_SUBCLASS || protocol != ADB_PROTOCOL) {
+ if(if_class != ADB_CLASS || subclass != ADB_SUBCLASS || protocol != ADB_PROTOCOL) {
// Ignore non-ADB devices.
- DBG("Ignoring interface with incorrect class/subclass/protocol - %d, %d, %d\n", class, subclass, protocol);
+ DBG("Ignoring interface with incorrect class/subclass/protocol - %d, %d, %d\n", if_class, subclass, protocol);
(*iface)->Release(iface);
continue;
}
@@ -176,7 +178,7 @@
}
result = (*plugInInterface)->QueryInterface(plugInInterface,
- CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID) &dev);
+ CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*)&dev);
//* only needed this to query the plugin
(*plugInInterface)->Release(plugInInterface);
if (result || !dev) {
@@ -333,7 +335,8 @@
interfaceSubClass, interfaceProtocol))
goto err_bad_adb_interface;
- handle = calloc(1, sizeof(usb_handle));
+ handle = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
+ if (handle == nullptr) goto err_bad_adb_interface;
//* Iterate over the endpoints for this interface and find the first
//* bulk in/out pipes available. These will be our read/write pipes.
@@ -395,22 +398,27 @@
IONotificationPortDestroy(notificationPort);
DBG("RunLoopThread done\n");
- return NULL;
+ return NULL;
}
+static void usb_cleanup() {
+ DBG("usb_cleanup\n");
+ close_usb_devices();
+ if (currentRunLoop)
+ CFRunLoopStop(currentRunLoop);
+}
-static int initialized = 0;
-void usb_init()
-{
- if (!initialized)
- {
- adb_thread_t tid;
+void usb_init() {
+ static bool initialized = false;
+ if (!initialized) {
+ atexit(usb_cleanup);
adb_mutex_init(&start_lock, NULL);
adb_cond_init(&start_cond, NULL);
- if(adb_thread_create(&tid, RunLoopThread, NULL))
+ if (!adb_thread_create(RunLoopThread, nullptr)) {
fatal_errno("cannot create input thread");
+ }
// Wait for initialization to finish
adb_mutex_lock(&start_lock);
@@ -420,18 +428,10 @@
adb_mutex_destroy(&start_lock);
adb_cond_destroy(&start_cond);
- initialized = 1;
+ initialized = true;
}
}
-void usb_cleanup()
-{
- DBG("usb_cleanup\n");
- close_usb_devices();
- if (currentRunLoop)
- CFRunLoopStop(currentRunLoop);
-}
-
int usb_write(usb_handle *handle, const void *buf, int len)
{
IOReturn result;
diff --git a/adb/usb_windows.cpp b/adb/usb_windows.cpp
index 3c5533b..25deb1b 100644
--- a/adb/usb_windows.cpp
+++ b/adb/usb_windows.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#define TRACE_TAG TRACE_USB
+
+#include "sysdeps.h"
+
#include <winsock2.h> // winsock.h *must* be included before windows.h.
#include <adb_api.h>
#include <errno.h>
@@ -23,9 +27,6 @@
#include <windows.h>
#include <winerror.h>
-#include "sysdeps.h"
-
-#define TRACE_TAG TRACE_USB
#include "adb.h"
#include "transport.h"
@@ -92,9 +93,6 @@
/// Initializes this module
void usb_init();
-/// Cleans up this module
-void usb_cleanup();
-
/// Opens usb interface (device) by interface (device) name.
usb_handle* do_usb_open(const wchar_t* interface_name);
@@ -180,16 +178,11 @@
}
void usb_init() {
- adb_thread_t tid;
-
- if(adb_thread_create(&tid, device_poll_thread, NULL)) {
+ if (!adb_thread_create(device_poll_thread, nullptr)) {
fatal_errno("cannot create input thread");
}
}
-void usb_cleanup() {
-}
-
usb_handle* do_usb_open(const wchar_t* interface_name) {
// Allocate our handle
usb_handle* ret = (usb_handle*)malloc(sizeof(usb_handle));
diff --git a/base/Android.mk b/base/Android.mk
index 0e1a9b6..7bd317b 100644
--- a/base/Android.mk
+++ b/base/Android.mk
@@ -18,13 +18,17 @@
libbase_src_files := \
file.cpp \
+ logging.cpp \
stringprintf.cpp \
strings.cpp \
libbase_test_src_files := \
file_test.cpp \
+ logging_test.cpp \
stringprintf_test.cpp \
strings_test.cpp \
+ test_main.cpp \
+ test_utils.cpp \
libbase_cppflags := \
-Wall \
@@ -40,6 +44,7 @@
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_CPPFLAGS := $(libbase_cppflags)
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := libcutils
LOCAL_MULTILIB := both
include $(BUILD_STATIC_LIBRARY)
@@ -49,6 +54,7 @@
LOCAL_WHOLE_STATIC_LIBRARIES := libbase
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_SHARED_LIBRARIES := libcutils
LOCAL_MULTILIB := both
include $(BUILD_SHARED_LIBRARY)
@@ -56,20 +62,20 @@
# ------------------------------------------------------------------------------
include $(CLEAR_VARS)
LOCAL_MODULE := libbase
-LOCAL_CLANG := true
LOCAL_SRC_FILES := $(libbase_src_files)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_CPPFLAGS := $(libbase_cppflags)
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := libcutils
LOCAL_MULTILIB := both
include $(BUILD_HOST_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libbase
-LOCAL_CLANG := true
LOCAL_WHOLE_STATIC_LIBRARIES := libbase
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := libcutils
LOCAL_MULTILIB := both
include $(BUILD_HOST_SHARED_LIBRARY)
@@ -79,6 +85,7 @@
LOCAL_MODULE := libbase_test
LOCAL_CLANG := true
LOCAL_SRC_FILES := $(libbase_test_src_files)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_CPPFLAGS := $(libbase_cppflags)
LOCAL_SHARED_LIBRARIES := libbase
LOCAL_MULTILIB := both
@@ -88,8 +95,8 @@
include $(CLEAR_VARS)
LOCAL_MODULE := libbase_test
-LOCAL_CLANG := true
LOCAL_SRC_FILES := $(libbase_test_src_files)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_CPPFLAGS := $(libbase_cppflags)
LOCAL_SHARED_LIBRARIES := libbase
LOCAL_MULTILIB := both
diff --git a/base/CPPLINT.cfg b/base/CPPLINT.cfg
index 5ee068e..d94a89c 100644
--- a/base/CPPLINT.cfg
+++ b/base/CPPLINT.cfg
@@ -1,2 +1,2 @@
set noparent
-filter=-build/header_guard
+filter=-build/header_guard,-build/include,-build/c++11,-whitespace/operators
diff --git a/base/file.cpp b/base/file.cpp
index 118071e..6b19818 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -26,6 +26,7 @@
#include "base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
#define LOG_TAG "base.file"
#include "cutils/log.h"
+#include "utils/Compat.h"
namespace android {
namespace base {
@@ -119,5 +120,29 @@
return result || CleanUpAfterFailedWrite(path);
}
+bool ReadFully(int fd, void* data, size_t byte_count) {
+ uint8_t* p = reinterpret_cast<uint8_t*>(data);
+ size_t remaining = byte_count;
+ while (remaining > 0) {
+ ssize_t n = TEMP_FAILURE_RETRY(read(fd, p, remaining));
+ if (n <= 0) return false;
+ p += n;
+ remaining -= n;
+ }
+ return true;
+}
+
+bool WriteFully(int fd, const void* data, size_t byte_count) {
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
+ size_t remaining = byte_count;
+ while (remaining > 0) {
+ ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, remaining));
+ if (n == -1) return false;
+ p += n;
+ remaining -= n;
+ }
+ return true;
+}
+
} // namespace base
} // namespace android
diff --git a/base/file_test.cpp b/base/file_test.cpp
index 34b8755..b138094 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -24,29 +24,7 @@
#include <string>
-class TemporaryFile {
- public:
- TemporaryFile() {
- init("/data/local/tmp");
- if (fd == -1) {
- init("/tmp");
- }
- }
-
- ~TemporaryFile() {
- close(fd);
- unlink(filename);
- }
-
- int fd;
- char filename[1024];
-
- private:
- void init(const char* tmp_dir) {
- snprintf(filename, sizeof(filename), "%s/TemporaryFile-XXXXXX", tmp_dir);
- fd = mkstemp(filename);
- }
-};
+#include "test_utils.h"
TEST(file, ReadFileToString_ENOENT) {
std::string s("hello");
@@ -58,7 +36,8 @@
TEST(file, ReadFileToString_success) {
std::string s("hello");
- ASSERT_TRUE(android::base::ReadFileToString("/proc/version", &s)) << errno;
+ ASSERT_TRUE(android::base::ReadFileToString("/proc/version", &s))
+ << strerror(errno);
EXPECT_GT(s.length(), 6U);
EXPECT_EQ('\n', s[s.length() - 1]);
s[5] = 0;
@@ -68,36 +47,69 @@
TEST(file, WriteStringToFile) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
- ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.filename)) << errno;
+ ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.filename))
+ << strerror(errno);
std::string s;
- ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s)) << errno;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s))
+ << strerror(errno);
EXPECT_EQ("abc", s);
}
+// WriteStringToFile2 is explicitly for setting Unix permissions, which make no
+// sense on Windows.
+#if !defined(_WIN32)
TEST(file, WriteStringToFile2) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.filename, 0660,
getuid(), getgid()))
- << errno;
+ << strerror(errno);
struct stat sb;
ASSERT_EQ(0, stat(tf.filename, &sb));
- ASSERT_EQ(0660U, (sb.st_mode & ~S_IFMT));
+ ASSERT_EQ(0660U, static_cast<unsigned int>(sb.st_mode & ~S_IFMT));
ASSERT_EQ(getuid(), sb.st_uid);
ASSERT_EQ(getgid(), sb.st_gid);
std::string s;
- ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s)) << errno;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s))
+ << strerror(errno);
EXPECT_EQ("abc", s);
}
+#endif
TEST(file, WriteStringToFd) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
- ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << errno;
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
std::string s;
- ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << errno;
+ ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
+ EXPECT_EQ("abc", s);
+}
+
+TEST(file, ReadFully) {
+ int fd = open("/proc/version", O_RDONLY);
+ ASSERT_NE(-1, fd) << strerror(errno);
+
+ char buf[1024];
+ memset(buf, 0, sizeof(buf));
+ ASSERT_TRUE(android::base::ReadFully(fd, buf, 5));
+ ASSERT_STREQ("Linux", buf);
+
+ ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
+
+ ASSERT_FALSE(android::base::ReadFully(fd, buf, sizeof(buf)));
+
+ close(fd);
+}
+
+TEST(file, WriteFully) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(android::base::WriteFully(tf.fd, "abc", 3));
+ std::string s;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s))
+ << strerror(errno);
EXPECT_EQ("abc", s);
}
diff --git a/base/include/base/file.h b/base/include/base/file.h
index ef97742..acd29b3 100644
--- a/base/include/base/file.h
+++ b/base/include/base/file.h
@@ -34,6 +34,9 @@
mode_t mode, uid_t owner, gid_t group);
#endif
+bool ReadFully(int fd, void* data, size_t byte_count);
+bool WriteFully(int fd, const void* data, size_t byte_count);
+
} // namespace base
} // namespace android
diff --git a/base/include/base/logging.h b/base/include/base/logging.h
new file mode 100644
index 0000000..4a15f43
--- /dev/null
+++ b/base/include/base/logging.h
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BASE_LOGGING_H
+#define BASE_LOGGING_H
+
+#ifdef ERROR
+#error ERROR is already defined. If this is Windows code, #define NOGDI before \
+including anything.
+#endif
+
+#ifdef _WIN32
+#ifndef NOGDI
+#define NOGDI // Suppress the evil ERROR macro.
+#endif
+#endif
+
+#include <functional>
+#include <memory>
+#include <ostream>
+
+#include "base/macros.h"
+
+namespace android {
+namespace base {
+
+enum LogSeverity {
+ VERBOSE,
+ DEBUG,
+ INFO,
+ WARNING,
+ ERROR,
+ FATAL,
+};
+
+enum LogId {
+ DEFAULT,
+ MAIN,
+ SYSTEM,
+};
+
+typedef std::function<void(LogId, LogSeverity, const char*, const char*,
+ unsigned int, const char*)> LogFunction;
+
+extern void StderrLogger(LogId, LogSeverity, const char*, const char*,
+ unsigned int, const char*);
+
+#ifdef __ANDROID__
+// We expose this even though it is the default because a user that wants to
+// override the default log buffer will have to construct this themselves.
+class LogdLogger {
+ public:
+ explicit LogdLogger(LogId default_log_id = android::base::MAIN);
+
+ void operator()(LogId, LogSeverity, const char* tag, const char* file,
+ unsigned int line, const char* message);
+
+ private:
+ LogId default_log_id_;
+};
+#endif
+
+// Configure logging based on ANDROID_LOG_TAGS environment variable.
+// We need to parse a string that looks like
+//
+// *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
+//
+// The tag (or '*' for the global level) comes first, followed by a colon and a
+// letter indicating the minimum priority level we're expected to log. This can
+// be used to reveal or conceal logs with specific tags.
+extern void InitLogging(char* argv[], LogFunction&& logger);
+
+// Configures logging using the default logger (logd for the device, stderr for
+// the host).
+extern void InitLogging(char* argv[]);
+
+// Replace the current logger.
+extern void SetLogger(LogFunction&& logger);
+
+// Logs a message to logcat on Android otherwise to stderr. If the severity is
+// FATAL it also causes an abort. For example:
+//
+// LOG(FATAL) << "We didn't expect to reach here";
+#define LOG(severity) \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
+ ::android::base::severity, -1).stream()
+
+// Logs a message to logcat with the specified log ID on Android otherwise to
+// stderr. If the severity is FATAL it also causes an abort.
+#define LOG_TO(dest, severity) \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest, \
+ ::android::base::severity, -1).stream()
+
+// A variant of LOG that also logs the current errno value. To be used when
+// library calls fail.
+#define PLOG(severity) \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
+ ::android::base::severity, errno).stream()
+
+// Behaves like PLOG, but logs to the specified log ID.
+#define PLOG_TO(dest, severity) \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest, \
+ ::android::base::severity, errno).stream()
+
+// Marker that code is yet to be implemented.
+#define UNIMPLEMENTED(level) \
+ LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
+
+// Check whether condition x holds and LOG(FATAL) if not. The value of the
+// expression x is only evaluated once. Extra logging can be appended using <<
+// after. For example:
+//
+// CHECK(false == true) results in a log message of
+// "Check failed: false == true".
+#define CHECK(x) \
+ if (UNLIKELY(!(x))) \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
+ ::android::base::FATAL, -1).stream() \
+ << "Check failed: " #x << " "
+
+// Helper for CHECK_xx(x,y) macros.
+#define CHECK_OP(LHS, RHS, OP) \
+ for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS); \
+ UNLIKELY(!(_values.lhs OP _values.rhs)); \
+ /* empty */) \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
+ ::android::base::FATAL, -1).stream() \
+ << "Check failed: " << #LHS << " " << #OP << " " << #RHS \
+ << " (" #LHS "=" << _values.lhs << ", " #RHS "=" << _values.rhs << ") "
+
+// Check whether a condition holds between x and y, LOG(FATAL) if not. The value
+// of the expressions x and y is evaluated once. Extra logging can be appended
+// using << after. For example:
+//
+// CHECK_NE(0 == 1, false) results in
+// "Check failed: false != false (0==1=false, false=false) ".
+#define CHECK_EQ(x, y) CHECK_OP(x, y, == )
+#define CHECK_NE(x, y) CHECK_OP(x, y, != )
+#define CHECK_LE(x, y) CHECK_OP(x, y, <= )
+#define CHECK_LT(x, y) CHECK_OP(x, y, < )
+#define CHECK_GE(x, y) CHECK_OP(x, y, >= )
+#define CHECK_GT(x, y) CHECK_OP(x, y, > )
+
+// Helper for CHECK_STRxx(s1,s2) macros.
+#define CHECK_STROP(s1, s2, sense) \
+ if (UNLIKELY((strcmp(s1, s2) == 0) != sense)) \
+ LOG(FATAL) << "Check failed: " \
+ << "\"" << s1 << "\"" \
+ << (sense ? " == " : " != ") << "\"" << s2 << "\""
+
+// Check for string (const char*) equality between s1 and s2, LOG(FATAL) if not.
+#define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true)
+#define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false)
+
+// Perform the pthread function call(args), LOG(FATAL) on error.
+#define CHECK_PTHREAD_CALL(call, args, what) \
+ do { \
+ int rc = call args; \
+ if (rc != 0) { \
+ errno = rc; \
+ PLOG(FATAL) << #call << " failed for " << what; \
+ } \
+ } while (false)
+
+// CHECK that can be used in a constexpr function. For example:
+//
+// constexpr int half(int n) {
+// return
+// DCHECK_CONSTEXPR(n >= 0, , 0)
+// CHECK_CONSTEXPR((n & 1) == 0),
+// << "Extra debugging output: n = " << n, 0)
+// n / 2;
+// }
+#define CHECK_CONSTEXPR(x, out, dummy) \
+ (UNLIKELY(!(x))) \
+ ? (LOG(FATAL) << "Check failed: " << #x out, dummy) \
+ :
+
+// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally
+// CHECK should be used unless profiling identifies a CHECK as being in
+// performance critical code.
+#if defined(NDEBUG)
+static constexpr bool kEnableDChecks = false;
+#else
+static constexpr bool kEnableDChecks = true;
+#endif
+
+#define DCHECK(x) \
+ if (::android::base::kEnableDChecks) CHECK(x)
+#define DCHECK_EQ(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_EQ(x, y)
+#define DCHECK_NE(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_NE(x, y)
+#define DCHECK_LE(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_LE(x, y)
+#define DCHECK_LT(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_LT(x, y)
+#define DCHECK_GE(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_GE(x, y)
+#define DCHECK_GT(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_GT(x, y)
+#define DCHECK_STREQ(s1, s2) \
+ if (::android::base::kEnableDChecks) CHECK_STREQ(s1, s2)
+#define DCHECK_STRNE(s1, s2) \
+ if (::android::base::kEnableDChecks) CHECK_STRNE(s1, s2)
+#if defined(NDEBUG)
+#define DCHECK_CONSTEXPR(x, out, dummy)
+#else
+#define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy)
+#endif
+
+// Temporary class created to evaluate the LHS and RHS, used with
+// MakeEagerEvaluator to infer the types of LHS and RHS.
+template <typename LHS, typename RHS>
+struct EagerEvaluator {
+ EagerEvaluator(LHS l, RHS r) : lhs(l), rhs(r) {
+ }
+ LHS lhs;
+ RHS rhs;
+};
+
+// Helper function for CHECK_xx.
+template <typename LHS, typename RHS>
+static inline EagerEvaluator<LHS, RHS> MakeEagerEvaluator(LHS lhs, RHS rhs) {
+ return EagerEvaluator<LHS, RHS>(lhs, rhs);
+}
+
+// Explicitly instantiate EagerEvalue for pointers so that char*s aren't treated
+// as strings. To compare strings use CHECK_STREQ and CHECK_STRNE. We rely on
+// signed/unsigned warnings to protect you against combinations not explicitly
+// listed below.
+#define EAGER_PTR_EVALUATOR(T1, T2) \
+ template <> \
+ struct EagerEvaluator<T1, T2> { \
+ EagerEvaluator(T1 l, T2 r) \
+ : lhs(reinterpret_cast<const void*>(l)), \
+ rhs(reinterpret_cast<const void*>(r)) { \
+ } \
+ const void* lhs; \
+ const void* rhs; \
+ }
+EAGER_PTR_EVALUATOR(const char*, const char*);
+EAGER_PTR_EVALUATOR(const char*, char*);
+EAGER_PTR_EVALUATOR(char*, const char*);
+EAGER_PTR_EVALUATOR(char*, char*);
+EAGER_PTR_EVALUATOR(const unsigned char*, const unsigned char*);
+EAGER_PTR_EVALUATOR(const unsigned char*, unsigned char*);
+EAGER_PTR_EVALUATOR(unsigned char*, const unsigned char*);
+EAGER_PTR_EVALUATOR(unsigned char*, unsigned char*);
+EAGER_PTR_EVALUATOR(const signed char*, const signed char*);
+EAGER_PTR_EVALUATOR(const signed char*, signed char*);
+EAGER_PTR_EVALUATOR(signed char*, const signed char*);
+EAGER_PTR_EVALUATOR(signed char*, signed char*);
+
+// Data for the log message, not stored in LogMessage to avoid increasing the
+// stack size.
+class LogMessageData;
+
+// A LogMessage is a temporarily scoped object used by LOG and the unlikely part
+// of a CHECK. The destructor will abort if the severity is FATAL.
+class LogMessage {
+ public:
+ LogMessage(const char* file, unsigned int line, LogId id,
+ LogSeverity severity, int error);
+
+ ~LogMessage();
+
+ // Returns the stream associated with the message, the LogMessage performs
+ // output when it goes out of scope.
+ std::ostream& stream();
+
+ // The routine that performs the actual logging.
+ static void LogLine(const char* file, unsigned int line, LogId id,
+ LogSeverity severity, const char* msg);
+
+ private:
+ const std::unique_ptr<LogMessageData> data_;
+
+ DISALLOW_COPY_AND_ASSIGN(LogMessage);
+};
+
+// Allows to temporarily change the minimum severity level for logging.
+class ScopedLogSeverity {
+ public:
+ explicit ScopedLogSeverity(LogSeverity level);
+ ~ScopedLogSeverity();
+
+ private:
+ LogSeverity old_;
+};
+
+} // namespace base
+} // namespace android
+
+#endif // BASE_LOGGING_H
diff --git a/base/include/base/memory.h b/base/include/base/memory.h
new file mode 100644
index 0000000..882582f
--- /dev/null
+++ b/base/include/base/memory.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BASE_MEMORY_H
+#define BASE_MEMORY_H
+
+namespace android {
+namespace base {
+
+// Use packed structures for access to unaligned data on targets with alignment
+// restrictions. The compiler will generate appropriate code to access these
+// structures without generating alignment exceptions.
+template <typename T>
+static inline T get_unaligned(const T* address) {
+ struct unaligned {
+ T v;
+ } __attribute__((packed));
+ const unaligned* p = reinterpret_cast<const unaligned*>(address);
+ return p->v;
+}
+
+template <typename T>
+static inline void put_unaligned(T* address, T v) {
+ struct unaligned {
+ T v;
+ } __attribute__((packed));
+ unaligned* p = reinterpret_cast<unaligned*>(address);
+ p->v = v;
+}
+
+} // namespace base
+} // namespace android
+
+#endif // BASE_MEMORY_H
diff --git a/base/include/base/strings.h b/base/include/base/strings.h
index 5ddfbbd..5dbc5fb 100644
--- a/base/include/base/strings.h
+++ b/base/include/base/strings.h
@@ -23,10 +23,13 @@
namespace android {
namespace base {
-// Splits a string using the given separator character into a vector of strings.
-// Empty strings will be omitted.
-void Split(const std::string& s, char separator,
- std::vector<std::string>* result);
+// Splits a string into a vector of strings.
+//
+// The string is split at each occurrence of a character in delimiters.
+//
+// The empty string is not a valid delimiter list.
+std::vector<std::string> Split(const std::string& s,
+ const std::string& delimiters);
// Trims whitespace off both ends of the given string.
std::string Trim(const std::string& s);
diff --git a/base/logging.cpp b/base/logging.cpp
new file mode 100644
index 0000000..7a08c39
--- /dev/null
+++ b/base/logging.cpp
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/logging.h"
+
+#include <libgen.h>
+
+// For getprogname(3) or program_invocation_short_name.
+#if defined(__ANDROID__) || defined(__APPLE__)
+#include <stdlib.h>
+#elif defined(__GLIBC__)
+#include <errno.h>
+#endif
+
+#include <iostream>
+#include <limits>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#ifndef _WIN32
+#include <mutex>
+#else
+#include <windows.h>
+#endif
+
+#include "base/macros.h"
+#include "base/strings.h"
+#include "cutils/threads.h"
+
+// Headers for LogMessage::LogLine.
+#ifdef __ANDROID__
+#include <android/set_abort_message.h>
+#include "cutils/log.h"
+#else
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+namespace {
+#ifndef _WIN32
+using std::mutex;
+using std::lock_guard;
+
+#if defined(__GLIBC__)
+const char* getprogname() {
+ return program_invocation_short_name;
+}
+#endif
+
+#else
+const char* getprogname() {
+ static bool first = true;
+ static char progname[MAX_PATH] = {};
+
+ if (first) {
+ // TODO(danalbert): This is a full path on Windows. Just get the basename.
+ DWORD nchars = GetModuleFileName(nullptr, progname, sizeof(progname));
+ DCHECK_GT(nchars, 0U);
+ first = false;
+ }
+
+ return progname;
+}
+
+class mutex {
+ public:
+ mutex() {
+ semaphore_ = CreateSemaphore(nullptr, 1, 1, nullptr);
+ CHECK(semaphore_ != nullptr) << "Failed to create Mutex";
+ }
+ ~mutex() {
+ CloseHandle(semaphore_);
+ }
+
+ void lock() {
+ DWORD result = WaitForSingleObject(semaphore_, INFINITE);
+ CHECK_EQ(result, WAIT_OBJECT_0) << GetLastError();
+ }
+
+ void unlock() {
+ bool result = ReleaseSemaphore(semaphore_, 1, nullptr);
+ CHECK(result);
+ }
+
+ private:
+ HANDLE semaphore_;
+};
+
+template <typename LockT>
+class lock_guard {
+ public:
+ explicit lock_guard(LockT& lock) : lock_(lock) {
+ lock_.lock();
+ }
+
+ ~lock_guard() {
+ lock_.unlock();
+ }
+
+ private:
+ LockT& lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(lock_guard);
+};
+#endif
+} // namespace
+
+namespace android {
+namespace base {
+
+static mutex logging_lock;
+
+#ifdef __ANDROID__
+static LogFunction gLogger = LogdLogger();
+#else
+static LogFunction gLogger = StderrLogger;
+#endif
+
+static bool gInitialized = false;
+static LogSeverity gMinimumLogSeverity = INFO;
+static std::unique_ptr<std::string> gProgramInvocationName;
+
+static const char* ProgramInvocationName() {
+ if (gProgramInvocationName == nullptr) {
+ gProgramInvocationName.reset(new std::string(getprogname()));
+ }
+
+ return gProgramInvocationName->c_str();
+}
+
+void StderrLogger(LogId, LogSeverity severity, const char*, const char* file,
+ unsigned int line, const char* message) {
+ static const char* log_characters = "VDIWEF";
+ CHECK_EQ(strlen(log_characters), FATAL + 1U);
+ char severity_char = log_characters[severity];
+ fprintf(stderr, "%s %c %5d %5d %s:%u] %s\n", ProgramInvocationName(),
+ severity_char, getpid(), gettid(), file, line, message);
+}
+
+
+#ifdef __ANDROID__
+LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) {
+}
+
+static const android_LogPriority kLogSeverityToAndroidLogPriority[] = {
+ ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO,
+ ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL,
+};
+static_assert(arraysize(kLogSeverityToAndroidLogPriority) == FATAL + 1,
+ "Mismatch in size of kLogSeverityToAndroidLogPriority and values "
+ "in LogSeverity");
+
+static const log_id kLogIdToAndroidLogId[] = {
+ LOG_ID_MAX, LOG_ID_MAIN, LOG_ID_SYSTEM,
+};
+static_assert(arraysize(kLogIdToAndroidLogId) == SYSTEM + 1,
+ "Mismatch in size of kLogIdToAndroidLogId and values in LogId");
+
+void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag,
+ const char* file, unsigned int line,
+ const char* message) {
+ int priority = kLogSeverityToAndroidLogPriority[severity];
+ if (id == DEFAULT) {
+ id = default_log_id_;
+ }
+
+ log_id lg_id = kLogIdToAndroidLogId[id];
+
+ if (priority == ANDROID_LOG_FATAL) {
+ __android_log_buf_print(lg_id, priority, tag, "%s:%u] %s", file, line,
+ message);
+ } else {
+ __android_log_buf_print(lg_id, priority, tag, "%s", message);
+ }
+}
+#endif
+
+void InitLogging(char* argv[], LogFunction&& logger) {
+ SetLogger(std::forward<LogFunction>(logger));
+ InitLogging(argv);
+}
+
+void InitLogging(char* argv[]) {
+ if (gInitialized) {
+ return;
+ }
+
+ gInitialized = true;
+
+ // Stash the command line for later use. We can use /proc/self/cmdline on
+ // Linux to recover this, but we don't have that luxury on the Mac, and there
+ // are a couple of argv[0] variants that are commonly used.
+ if (argv != nullptr) {
+ gProgramInvocationName.reset(new std::string(basename(argv[0])));
+ }
+
+ const char* tags = getenv("ANDROID_LOG_TAGS");
+ if (tags == nullptr) {
+ return;
+ }
+
+ std::vector<std::string> specs = Split(tags, " ");
+ for (size_t i = 0; i < specs.size(); ++i) {
+ // "tag-pattern:[vdiwefs]"
+ std::string spec(specs[i]);
+ if (spec.size() == 3 && StartsWith(spec, "*:")) {
+ switch (spec[2]) {
+ case 'v':
+ gMinimumLogSeverity = VERBOSE;
+ continue;
+ case 'd':
+ gMinimumLogSeverity = DEBUG;
+ continue;
+ case 'i':
+ gMinimumLogSeverity = INFO;
+ continue;
+ case 'w':
+ gMinimumLogSeverity = WARNING;
+ continue;
+ case 'e':
+ gMinimumLogSeverity = ERROR;
+ continue;
+ case 'f':
+ gMinimumLogSeverity = FATAL;
+ continue;
+ // liblog will even suppress FATAL if you say 's' for silent, but that's
+ // crazy!
+ case 's':
+ gMinimumLogSeverity = FATAL;
+ continue;
+ }
+ }
+ LOG(FATAL) << "unsupported '" << spec << "' in ANDROID_LOG_TAGS (" << tags
+ << ")";
+ }
+}
+
+void SetLogger(LogFunction&& logger) {
+ lock_guard<mutex> lock(logging_lock);
+ gLogger = std::move(logger);
+}
+
+// This indirection greatly reduces the stack impact of having lots of
+// checks/logging in a function.
+class LogMessageData {
+ public:
+ LogMessageData(const char* file, unsigned int line, LogId id,
+ LogSeverity severity, int error)
+ : file_(file),
+ line_number_(line),
+ id_(id),
+ severity_(severity),
+ error_(error) {
+ const char* last_slash = strrchr(file, '/');
+ file = (last_slash == nullptr) ? file : last_slash + 1;
+ }
+
+ const char* GetFile() const {
+ return file_;
+ }
+
+ unsigned int GetLineNumber() const {
+ return line_number_;
+ }
+
+ LogSeverity GetSeverity() const {
+ return severity_;
+ }
+
+ LogId GetId() const {
+ return id_;
+ }
+
+ int GetError() const {
+ return error_;
+ }
+
+ std::ostream& GetBuffer() {
+ return buffer_;
+ }
+
+ std::string ToString() const {
+ return buffer_.str();
+ }
+
+ private:
+ std::ostringstream buffer_;
+ const char* const file_;
+ const unsigned int line_number_;
+ const LogId id_;
+ const LogSeverity severity_;
+ const int error_;
+
+ DISALLOW_COPY_AND_ASSIGN(LogMessageData);
+};
+
+LogMessage::LogMessage(const char* file, unsigned int line, LogId id,
+ LogSeverity severity, int error)
+ : data_(new LogMessageData(file, line, id, severity, error)) {
+}
+
+LogMessage::~LogMessage() {
+ if (data_->GetSeverity() < gMinimumLogSeverity) {
+ return; // No need to format something we're not going to output.
+ }
+
+ // Finish constructing the message.
+ if (data_->GetError() != -1) {
+ data_->GetBuffer() << ": " << strerror(data_->GetError());
+ }
+ std::string msg(data_->ToString());
+
+ if (msg.find('\n') == std::string::npos) {
+ LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
+ data_->GetSeverity(), msg.c_str());
+ } else {
+ msg += '\n';
+ size_t i = 0;
+ while (i < msg.size()) {
+ size_t nl = msg.find('\n', i);
+ msg[nl] = '\0';
+ LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
+ data_->GetSeverity(), &msg[i]);
+ i = nl + 1;
+ }
+ }
+
+ // Abort if necessary.
+ if (data_->GetSeverity() == FATAL) {
+#ifdef __ANDROID__
+ android_set_abort_message(msg.c_str());
+#endif
+ abort();
+ }
+}
+
+std::ostream& LogMessage::stream() {
+ return data_->GetBuffer();
+}
+
+void LogMessage::LogLine(const char* file, unsigned int line, LogId id,
+ LogSeverity severity, const char* message) {
+ const char* tag = ProgramInvocationName();
+ lock_guard<mutex> lock(logging_lock);
+ gLogger(id, severity, tag, file, line, message);
+}
+
+ScopedLogSeverity::ScopedLogSeverity(LogSeverity level) {
+ old_ = gMinimumLogSeverity;
+ gMinimumLogSeverity = level;
+}
+
+ScopedLogSeverity::~ScopedLogSeverity() {
+ gMinimumLogSeverity = old_;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
new file mode 100644
index 0000000..c91857a
--- /dev/null
+++ b/base/logging_test.cpp
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/logging.h"
+
+#include <regex>
+#include <string>
+
+#include "base/file.h"
+#include "base/stringprintf.h"
+#include "test_utils.h"
+
+#include <gtest/gtest.h>
+
+#ifdef __ANDROID__
+#define HOST_TEST(suite, name) TEST(suite, DISABLED_ ## name)
+#else
+#define HOST_TEST(suite, name) TEST(suite, name)
+#endif
+
+class CapturedStderr {
+ public:
+ CapturedStderr() : old_stderr_(-1) {
+ init();
+ }
+
+ ~CapturedStderr() {
+ reset();
+ }
+
+ int fd() const {
+ return temp_file_.fd;
+ }
+
+ private:
+ void init() {
+ old_stderr_ = dup(STDERR_FILENO);
+ ASSERT_NE(-1, old_stderr_);
+ ASSERT_NE(-1, dup2(fd(), STDERR_FILENO));
+ }
+
+ void reset() {
+ ASSERT_NE(-1, dup2(old_stderr_, STDERR_FILENO));
+ ASSERT_EQ(0, close(old_stderr_));
+ }
+
+ TemporaryFile temp_file_;
+ int old_stderr_;
+};
+
+TEST(logging, CHECK) {
+ ASSERT_DEATH(CHECK(false), "Check failed: false ");
+ CHECK(true);
+
+ ASSERT_DEATH(CHECK_EQ(0, 1), "Check failed: 0 == 1 ");
+ CHECK_EQ(0, 0);
+
+ ASSERT_DEATH(CHECK_STREQ("foo", "bar"), R"(Check failed: "foo" == "bar")");
+ CHECK_STREQ("foo", "foo");
+}
+
+std::string make_log_pattern(android::base::LogSeverity severity,
+ const char* message) {
+ static const char* log_characters = "VDIWEF";
+ char log_char = log_characters[severity];
+ return android::base::StringPrintf(
+ "%c[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+ " __FILE__
+ ":[[:digit:]]+] %s",
+ log_char, message);
+}
+
+TEST(logging, LOG) {
+ ASSERT_DEATH(LOG(FATAL) << "foobar", "foobar");
+
+ // We can't usefully check the output of any of these on Windows because we
+ // don't have std::regex, but we can at least make sure we printed at least as
+ // many characters are in the log message.
+ {
+ CapturedStderr cap;
+ LOG(WARNING) << "foobar";
+ ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+
+ std::string output;
+ android::base::ReadFdToString(cap.fd(), &output);
+ ASSERT_GT(output.length(), strlen("foobar"));
+
+#if !defined(_WIN32)
+ std::regex message_regex(
+ make_log_pattern(android::base::WARNING, "foobar"));
+ ASSERT_TRUE(std::regex_search(output, message_regex));
+#endif
+ }
+
+ {
+ CapturedStderr cap;
+ LOG(INFO) << "foobar";
+ ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+
+ std::string output;
+ android::base::ReadFdToString(cap.fd(), &output);
+ ASSERT_GT(output.length(), strlen("foobar"));
+
+#if !defined(_WIN32)
+ std::regex message_regex(
+ make_log_pattern(android::base::INFO, "foobar"));
+ ASSERT_TRUE(std::regex_search(output, message_regex));
+#endif
+ }
+
+ {
+ CapturedStderr cap;
+ LOG(DEBUG) << "foobar";
+ ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+
+ std::string output;
+ android::base::ReadFdToString(cap.fd(), &output);
+ ASSERT_TRUE(output.empty());
+ }
+
+ {
+ android::base::ScopedLogSeverity severity(android::base::DEBUG);
+ CapturedStderr cap;
+ LOG(DEBUG) << "foobar";
+ ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+
+ std::string output;
+ android::base::ReadFdToString(cap.fd(), &output);
+ ASSERT_GT(output.length(), strlen("foobar"));
+
+#if !defined(_WIN32)
+ std::regex message_regex(
+ make_log_pattern(android::base::DEBUG, "foobar"));
+ ASSERT_TRUE(std::regex_search(output, message_regex));
+#endif
+ }
+}
+
+TEST(logging, PLOG) {
+ {
+ CapturedStderr cap;
+ errno = ENOENT;
+ PLOG(INFO) << "foobar";
+ ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+
+ std::string output;
+ android::base::ReadFdToString(cap.fd(), &output);
+ ASSERT_GT(output.length(), strlen("foobar"));
+
+#if !defined(_WIN32)
+ std::regex message_regex(make_log_pattern(
+ android::base::INFO, "foobar: No such file or directory"));
+ ASSERT_TRUE(std::regex_search(output, message_regex));
+#endif
+ }
+}
+
+TEST(logging, UNIMPLEMENTED) {
+ {
+ CapturedStderr cap;
+ errno = ENOENT;
+ UNIMPLEMENTED(ERROR);
+ ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+
+ std::string output;
+ android::base::ReadFdToString(cap.fd(), &output);
+ ASSERT_GT(output.length(), strlen("unimplemented"));
+
+#if !defined(_WIN32)
+ std::string expected_message =
+ android::base::StringPrintf("%s unimplemented ", __PRETTY_FUNCTION__);
+ std::regex message_regex(
+ make_log_pattern(android::base::ERROR, expected_message.c_str()));
+ ASSERT_TRUE(std::regex_search(output, message_regex));
+#endif
+ }
+}
diff --git a/base/stringprintf_test.cpp b/base/stringprintf_test.cpp
index 5cc2086..54b2b6c 100644
--- a/base/stringprintf_test.cpp
+++ b/base/stringprintf_test.cpp
@@ -20,11 +20,14 @@
#include <string>
+// The z size sepcifier isn't supported on Windows, so this test isn't useful.
+#if !defined(_WIN32)
TEST(StringPrintfTest, HexSizeT) {
size_t size = 0x00107e59;
EXPECT_EQ("00107e59", android::base::StringPrintf("%08zx", size));
EXPECT_EQ("0x00107e59", android::base::StringPrintf("0x%08zx", size));
}
+#endif
TEST(StringPrintfTest, StringAppendF) {
std::string s("a");
diff --git a/base/strings.cpp b/base/strings.cpp
index 224a46f..d3375d9 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -16,27 +16,33 @@
#include "base/strings.h"
+#include <stdlib.h>
+#include <string.h>
+
#include <string>
#include <vector>
namespace android {
namespace base {
-void Split(const std::string& s, char separator,
- std::vector<std::string>* result) {
- const char* p = s.data();
- const char* end = p + s.size();
- while (p != end) {
- if (*p == separator) {
- ++p;
- } else {
- const char* start = p;
- while (++p != end && *p != separator) {
- // Skip to the next occurrence of the separator.
- }
- result->push_back(std::string(start, p - start));
- }
- }
+#define CHECK_NE(a, b) \
+ if ((a) == (b)) abort();
+
+std::vector<std::string> Split(const std::string& s,
+ const std::string& delimiters) {
+ CHECK_NE(delimiters.size(), 0U);
+
+ std::vector<std::string> result;
+
+ size_t base = 0;
+ size_t found;
+ do {
+ found = s.find_first_of(delimiters, base);
+ result.push_back(s.substr(base, found - base));
+ base = found + 1;
+ } while (found != s.npos);
+
+ return result;
}
std::string Trim(const std::string& s) {
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index 824598d..46a1ab5 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -22,21 +22,19 @@
#include <vector>
TEST(strings, split_empty) {
- std::vector<std::string> parts;
- android::base::Split("", '\0', &parts);
- ASSERT_EQ(0U, parts.size());
+ std::vector<std::string> parts = android::base::Split("", ",");
+ ASSERT_EQ(1U, parts.size());
+ ASSERT_EQ("", parts[0]);
}
TEST(strings, split_single) {
- std::vector<std::string> parts;
- android::base::Split("foo", ',', &parts);
+ std::vector<std::string> parts = android::base::Split("foo", ",");
ASSERT_EQ(1U, parts.size());
ASSERT_EQ("foo", parts[0]);
}
TEST(strings, split_simple) {
- std::vector<std::string> parts;
- android::base::Split("foo,bar,baz", ',', &parts);
+ std::vector<std::string> parts = android::base::Split("foo,bar,baz", ",");
ASSERT_EQ(3U, parts.size());
ASSERT_EQ("foo", parts[0]);
ASSERT_EQ("bar", parts[1]);
@@ -44,13 +42,37 @@
}
TEST(strings, split_with_empty_part) {
- std::vector<std::string> parts;
- android::base::Split("foo,,bar", ',', &parts);
+ std::vector<std::string> parts = android::base::Split("foo,,bar", ",");
+ ASSERT_EQ(3U, parts.size());
+ ASSERT_EQ("foo", parts[0]);
+ ASSERT_EQ("", parts[1]);
+ ASSERT_EQ("bar", parts[2]);
+}
+
+TEST(strings, split_null_char) {
+ std::vector<std::string> parts =
+ android::base::Split(std::string("foo\0bar", 7), std::string("\0", 1));
ASSERT_EQ(2U, parts.size());
ASSERT_EQ("foo", parts[0]);
ASSERT_EQ("bar", parts[1]);
}
+TEST(strings, split_any) {
+ std::vector<std::string> parts = android::base::Split("foo:bar,baz", ",:");
+ ASSERT_EQ(3U, parts.size());
+ ASSERT_EQ("foo", parts[0]);
+ ASSERT_EQ("bar", parts[1]);
+ ASSERT_EQ("baz", parts[2]);
+}
+
+TEST(strings, split_any_with_empty_part) {
+ std::vector<std::string> parts = android::base::Split("foo:,bar", ",:");
+ ASSERT_EQ(3U, parts.size());
+ ASSERT_EQ("foo", parts[0]);
+ ASSERT_EQ("", parts[1]);
+ ASSERT_EQ("bar", parts[2]);
+}
+
TEST(strings, trim_empty) {
ASSERT_EQ("", android::base::Trim(""));
}
diff --git a/base/test_main.cpp b/base/test_main.cpp
new file mode 100644
index 0000000..546923d
--- /dev/null
+++ b/base/test_main.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "base/logging.h"
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ android::base::InitLogging(argv, android::base::StderrLogger);
+ return RUN_ALL_TESTS();
+}
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
new file mode 100644
index 0000000..0517bc7
--- /dev/null
+++ b/base/test_utils.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "test_utils.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
+TemporaryFile::TemporaryFile() {
+#if defined(__ANDROID__)
+ init("/data/local/tmp");
+#elif defined(_WIN32)
+ char wd[MAX_PATH] = {};
+ _getcwd(wd, sizeof(wd));
+ init(wd);
+#else
+ init("/tmp");
+#endif
+}
+
+TemporaryFile::~TemporaryFile() {
+ close(fd);
+ unlink(filename);
+}
+
+void TemporaryFile::init(const char* tmp_dir) {
+ snprintf(filename, sizeof(filename), "%s/TemporaryFile-XXXXXX", tmp_dir);
+#if !defined(_WIN32)
+ fd = mkstemp(filename);
+#else
+ // Windows doesn't have mkstemp, and tmpfile creates the file in the root
+ // directory, requiring root (?!) permissions. We have to settle for mktemp.
+ if (mktemp(filename) == nullptr) {
+ abort();
+ }
+
+ fd = open(filename, O_RDWR | O_NOINHERIT | O_CREAT, _S_IREAD | _S_IWRITE);
+#endif
+}
diff --git a/base/test_utils.h b/base/test_utils.h
new file mode 100644
index 0000000..132d3a7
--- /dev/null
+++ b/base/test_utils.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TEST_UTILS_H
+#define TEST_UTILS_H
+
+class TemporaryFile {
+ public:
+ TemporaryFile();
+ ~TemporaryFile();
+
+ int fd;
+ char filename[1024];
+
+ private:
+ void init(const char* tmp_dir);
+};
+
+#endif // TEST_UTILS_H
diff --git a/cpio/Android.mk b/cpio/Android.mk
index 575beb2..2aa7297 100644
--- a/cpio/Android.mk
+++ b/cpio/Android.mk
@@ -10,6 +10,8 @@
LOCAL_CFLAGS := -Werror
+LOCAL_SHARED_LIBRARIES := libcutils
+
include $(BUILD_HOST_EXECUTABLE)
$(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE))
diff --git a/debuggerd/elf_utils.cpp b/debuggerd/elf_utils.cpp
index 764b9db..5ea03e7 100644
--- a/debuggerd/elf_utils.cpp
+++ b/debuggerd/elf_utils.cpp
@@ -29,6 +29,8 @@
#include "elf_utils.h"
+#define NOTE_ALIGN(size) ((size + 3) & ~3)
+
template <typename HdrType, typename PhdrType, typename NhdrType>
static bool get_build_id(
Backtrace* backtrace, uintptr_t base_addr, uint8_t* e_ident, std::string* build_id) {
@@ -60,7 +62,7 @@
addr += sizeof(nhdr);
if (nhdr.n_type == NT_GNU_BUILD_ID) {
// Skip the name (which is the owner and should be "GNU").
- addr += nhdr.n_namesz;
+ addr += NOTE_ALIGN(nhdr.n_namesz);
uint8_t build_id_data[128];
if (nhdr.n_namesz > sizeof(build_id_data)) {
ALOGE("Possible corrupted note, name size value is too large: %u",
@@ -80,7 +82,7 @@
} else {
// Move past the extra note data.
hdr_size -= sizeof(nhdr);
- size_t skip_bytes = nhdr.n_namesz + nhdr.n_descsz;
+ size_t skip_bytes = NOTE_ALIGN(nhdr.n_namesz) + NOTE_ALIGN(nhdr.n_descsz);
addr += skip_bytes;
if (hdr_size < skip_bytes) {
break;
diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp
index 094ab48..b7e6b17 100644
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/tombstone.cpp
@@ -371,7 +371,8 @@
} else {
line += '-';
}
- line += android::base::StringPrintf(" %8" PRIxPTR, it->end - it->start);
+ line += android::base::StringPrintf(" %8" PRIxPTR " %8" PRIxPTR,
+ it->offset, it->end - it->start);
if (it->name.length() > 0) {
line += " " + it->name;
std::string build_id;
@@ -379,6 +380,9 @@
line += " (BuildId: " + build_id + ")";
}
}
+ if (it->load_base != 0) {
+ line += android::base::StringPrintf(" (load base 0x%" PRIxPTR ")", it->load_base);
+ }
_LOG(log, logtype::MAPS, "%s\n", line.c_str());
}
if (print_fault_address_marker) {
@@ -792,7 +796,7 @@
*detach_failed = dump_crash(&log, pid, tid, signal, original_si_code, abort_msg_address,
dump_sibling_threads, total_sleep_time_usec);
- ALOGI("\nTombstone written to: %s\n", path);
+ _LOG(&log, logtype::BACKTRACE, "\nTombstone written to: %s\n", path);
// Either of these file descriptors can be -1, any error is ignored.
close(amfd);
diff --git a/debuggerd/utility.cpp b/debuggerd/utility.cpp
index e10feff..e722f82 100644
--- a/debuggerd/utility.cpp
+++ b/debuggerd/utility.cpp
@@ -26,25 +26,12 @@
#include <sys/wait.h>
#include <backtrace/Backtrace.h>
+#include <base/file.h>
#include <log/log.h>
const int SLEEP_TIME_USEC = 50000; // 0.05 seconds
const int MAX_TOTAL_SLEEP_USEC = 10000000; // 10 seconds
-static int write_to_am(int fd, const char* buf, int len) {
- int to_write = len;
- while (to_write > 0) {
- int written = TEMP_FAILURE_RETRY(write(fd, buf + len - to_write, to_write));
- if (written < 0) {
- // hard failure
- ALOGE("AM write failure (%d / %s)\n", errno, strerror(errno));
- return -1;
- }
- to_write -= written;
- }
- return len;
-}
-
// Whitelist output desired in the logcat output.
bool is_allowed_in_logcat(enum logtype ltype) {
if ((ltype == ERROR)
@@ -80,11 +67,11 @@
}
if (write_to_logcat) {
- __android_log_buf_write(LOG_ID_CRASH, ANDROID_LOG_INFO, LOG_TAG, buf);
+ __android_log_buf_write(LOG_ID_CRASH, ANDROID_LOG_FATAL, LOG_TAG, buf);
if (write_to_activitymanager) {
- int written = write_to_am(log->amfd, buf, len);
- if (written <= 0) {
+ if (!android::base::WriteFully(log->amfd, buf, len)) {
// timeout or other failure on write; stop informing the activity manager
+ ALOGE("AM write failed: %s", strerror(errno));
log->amfd = -1;
}
}
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index aa5b14a..dee471a 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -19,10 +19,11 @@
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg \
$(LOCAL_PATH)/../../extras/ext4_utils \
$(LOCAL_PATH)/../../extras/f2fs_utils
-LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c util.c fs.c
+LOCAL_SRC_FILES := protocol.c engine.c bootimg_utils.cpp fastboot.cpp util.c fs.c
LOCAL_MODULE := fastboot
LOCAL_MODULE_TAGS := debug
-LOCAL_CFLAGS += -std=gnu99 -Werror
+LOCAL_CONLYFLAGS += -std=gnu99
+LOCAL_CFLAGS += -Wall -Wextra -Werror
ifeq ($(HOST_OS),linux)
LOCAL_SRC_FILES += usb_linux.c util_linux.c
@@ -52,10 +53,13 @@
LOCAL_STATIC_LIBRARIES := \
$(EXTRA_STATIC_LIBS) \
- libzipfile \
+ libziparchive-host \
libext4_utils_host \
libsparse_host \
- libz
+ libutils \
+ liblog \
+ libz \
+ libbase
ifneq ($(HOST_OS),windows)
LOCAL_STATIC_LIBRARIES += libselinux
@@ -71,6 +75,16 @@
LOCAL_STATIC_LIBRARIES += libf2fs_utils_host libf2fs_ioutils_host libf2fs_dlutils_host
endif
+# libc++ not available on windows yet
+ifneq ($(HOST_OS),windows)
+ LOCAL_CXX_STL := libc++_static
+endif
+
+# Don't add anything here, we don't want additional shared dependencies
+# on the host fastboot tool, and shared libraries that link against libc++
+# will violate ODR
+LOCAL_SHARED_LIBRARIES :=
+
include $(BUILD_HOST_EXECUTABLE)
my_dist_files := $(LOCAL_BUILT_MODULE)
diff --git a/fastboot/bootimg.c b/fastboot/bootimg_utils.cpp
similarity index 95%
rename from fastboot/bootimg.c
rename to fastboot/bootimg_utils.cpp
index 240784f..d8905a6 100644
--- a/fastboot/bootimg.c
+++ b/fastboot/bootimg_utils.cpp
@@ -26,12 +26,12 @@
* SUCH DAMAGE.
*/
+#include "bootimg_utils.h"
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <bootimg.h>
-
void bootimg_set_cmdline(boot_img_hdr *h, const char *cmdline)
{
strcpy((char*) h->cmdline, cmdline);
@@ -47,7 +47,6 @@
unsigned ramdisk_actual;
unsigned second_actual;
unsigned page_mask;
- boot_img_hdr *hdr;
page_mask = page_size - 1;
@@ -57,9 +56,8 @@
*bootimg_size = page_size + kernel_actual + ramdisk_actual + second_actual;
- hdr = calloc(*bootimg_size, 1);
-
- if(hdr == 0) {
+ boot_img_hdr* hdr = reinterpret_cast<boot_img_hdr*>(calloc(*bootimg_size, 1));
+ if (hdr == 0) {
return hdr;
}
diff --git a/fastboot/bootimg_utils.h b/fastboot/bootimg_utils.h
new file mode 100644
index 0000000..b1a86cd
--- /dev/null
+++ b/fastboot/bootimg_utils.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FASTBOOT_BOOTIMG_UTILS_H_
+#define _FASTBOOT_BOOTIMG_UTILS_H_
+
+#include <bootimg.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void bootimg_set_cmdline(boot_img_hdr *h, const char *cmdline);
+boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size, unsigned kernel_offset,
+ void *ramdisk, unsigned ramdisk_size, unsigned ramdisk_offset,
+ void *second, unsigned second_size, unsigned second_offset,
+ unsigned page_size, unsigned base, unsigned tags_offset,
+ unsigned *bootimg_size);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/fastboot/engineering_key.p12 b/fastboot/engineering_key.p12
deleted file mode 100644
index d8183b0..0000000
--- a/fastboot/engineering_key.p12
+++ /dev/null
Binary files differ
diff --git a/fastboot/fastboot.c b/fastboot/fastboot.cpp
similarity index 90%
rename from fastboot/fastboot.c
rename to fastboot/fastboot.cpp
index fc544a6..e139bcd 100644
--- a/fastboot/fastboot.c
+++ b/fastboot/fastboot.cpp
@@ -44,10 +44,10 @@
#include <sys/types.h>
#include <unistd.h>
-#include <bootimg.h>
#include <sparse/sparse.h>
-#include <zipfile/zipfile.h>
+#include <ziparchive/zip_archive.h>
+#include "bootimg_utils.h"
#include "fastboot.h"
#include "fs.h"
@@ -59,14 +59,6 @@
char cur_product[FB_RESPONSE_SZ + 1];
-void bootimg_set_cmdline(boot_img_hdr *h, const char *cmdline);
-
-boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size, unsigned kernel_offset,
- void *ramdisk, unsigned ramdisk_size, unsigned ramdisk_offset,
- void *second, unsigned second_size, unsigned second_offset,
- unsigned page_size, unsigned base, unsigned tags_offset,
- unsigned *bootimg_size);
-
static usb_handle *usb = 0;
static const char *serial = 0;
static const char *product = 0;
@@ -106,12 +98,10 @@
{"vendor.img", "vendor.sig", "vendor", true},
};
-void get_my_path(char *path);
-
char *find_item(const char *item, const char *product)
{
char *dir;
- char *fn;
+ const char *fn;
char path[PATH_MAX + 128];
if(!strcmp(item,"boot")) {
@@ -234,7 +224,7 @@
int list_devices_callback(usb_ifc_info *info)
{
if (match_fastboot_with_serial(info, NULL) == 0) {
- char* serial = info->serial_number;
+ const char* serial = info->serial_number;
if (!info->writable) {
serial = "no permissions"; // like "adb devices"
}
@@ -389,30 +379,26 @@
return bdata;
}
-void *unzip_file(zipfile_t zip, const char *name, unsigned *sz)
+static void* unzip_file(ZipArchiveHandle zip, const char* entry_name, unsigned* sz)
{
- void *data;
- zipentry_t entry;
- unsigned datasz;
-
- entry = lookup_zipentry(zip, name);
- if (entry == NULL) {
- fprintf(stderr, "archive does not contain '%s'\n", name);
+ ZipEntryName zip_entry_name(entry_name);
+ ZipEntry zip_entry;
+ if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
+ fprintf(stderr, "archive does not contain '%s'\n", entry_name);
return 0;
}
- *sz = get_zipentry_size(entry);
+ *sz = zip_entry.uncompressed_length;
- datasz = *sz * 1.001;
- data = malloc(datasz);
-
- if(data == 0) {
- fprintf(stderr, "failed to allocate %d bytes\n", *sz);
+ uint8_t* data = reinterpret_cast<uint8_t*>(malloc(zip_entry.uncompressed_length));
+ if (data == NULL) {
+ fprintf(stderr, "failed to allocate %u bytes for '%s'\n", *sz, entry_name);
return 0;
}
- if (decompress_zipentry(entry, data, datasz)) {
- fprintf(stderr, "failed to unzip '%s' from archive\n", name);
+ int error = ExtractToMemory(zip, &zip_entry, data, zip_entry.uncompressed_length);
+ if (error != 0) {
+ fprintf(stderr, "failed to extract '%s': %s\n", entry_name, ErrorCodeString(error));
free(data);
return 0;
}
@@ -420,27 +406,28 @@
return data;
}
-static int unzip_to_file(zipfile_t zip, char *name)
-{
- int fd;
- char *data;
- unsigned sz;
-
- fd = fileno(tmpfile());
- if (fd < 0) {
+static int unzip_to_file(ZipArchiveHandle zip, char* entry_name) {
+ FILE* fp = tmpfile();
+ if (fp == NULL) {
+ fprintf(stderr, "failed to create temporary file for '%s': %s\n",
+ entry_name, strerror(errno));
return -1;
}
- data = unzip_file(zip, name, &sz);
- if (data == 0) {
+ ZipEntryName zip_entry_name(entry_name);
+ ZipEntry zip_entry;
+ if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
+ fprintf(stderr, "archive does not contain '%s'\n", entry_name);
return -1;
}
- if (write(fd, data, sz) != (ssize_t)sz) {
- fd = -1;
+ int fd = fileno(fp);
+ int error = ExtractEntryToFile(zip, &zip_entry, fd);
+ if (error != 0) {
+ fprintf(stderr, "failed to extract '%s': %s\n", entry_name, ErrorCodeString(error));
+ return -1;
}
- free(data);
lseek(fd, 0, SEEK_SET);
return fd;
}
@@ -461,7 +448,6 @@
static int setup_requirement_line(char *name)
{
char *val[MAX_OPTIONS];
- const char **out;
char *prod = NULL;
unsigned n, count;
char *x;
@@ -501,10 +487,11 @@
name = strip(name);
if (name == 0) return -1;
- /* work around an unfortunate name mismatch */
- if (!strcmp(name,"board")) name = "product";
+ const char* var = name;
+ // Work around an unfortunate name mismatch.
+ if (!strcmp(name,"board")) var = "product";
- out = malloc(sizeof(char*) * count);
+ const char** out = reinterpret_cast<const char**>(malloc(sizeof(char*) * count));
if (out == 0) return -1;
for(n = 0; n < count; n++) {
@@ -518,7 +505,7 @@
}
}
- fb_queue_require(prod, name, invert, n, out);
+ fb_queue_require(prod, var, invert, n, out);
return 0;
}
@@ -551,21 +538,17 @@
static struct sparse_file **load_sparse_files(int fd, int max_size)
{
- struct sparse_file *s;
- int files;
- struct sparse_file **out_s;
-
- s = sparse_file_import_auto(fd, false);
+ struct sparse_file* s = sparse_file_import_auto(fd, false, true);
if (!s) {
die("cannot sparse read file\n");
}
- files = sparse_file_resparse(s, max_size, NULL, 0);
+ int files = sparse_file_resparse(s, max_size, NULL, 0);
if (files < 0) {
die("Failed to resparse\n");
}
- out_s = calloc(sizeof(struct sparse_file *), files + 1);
+ sparse_file** out_s = reinterpret_cast<sparse_file**>(calloc(sizeof(struct sparse_file *), files + 1));
if (!out_s) {
die("Failed to allocate sparse file array\n");
}
@@ -682,11 +665,11 @@
static void flash_buf(const char *pname, struct fastboot_buffer *buf)
{
- struct sparse_file **s;
+ sparse_file** s;
switch (buf->type) {
case FB_BUFFER_SPARSE:
- s = buf->data;
+ s = reinterpret_cast<sparse_file**>(buf->data);
while (*s) {
int64_t sz64 = sparse_file_len(*s, true, false);
fb_queue_flash_sparse(pname, *s++, sz64);
@@ -710,63 +693,44 @@
flash_buf(pname, &buf);
}
-void do_update_signature(zipfile_t zip, char *fn)
+void do_update_signature(ZipArchiveHandle zip, char *fn)
{
- void *data;
unsigned sz;
- data = unzip_file(zip, fn, &sz);
+ void* data = unzip_file(zip, fn, &sz);
if (data == 0) return;
fb_queue_download("signature", data, sz);
fb_queue_command("signature", "installing signature");
}
-void do_update(usb_handle *usb, char *fn, int erase_first)
+void do_update(usb_handle *usb, const char *filename, int erase_first)
{
- void *zdata;
- unsigned zsize;
- void *data;
- unsigned sz;
- zipfile_t zip;
- int fd;
- int rc;
- struct fastboot_buffer buf;
- size_t i;
-
queue_info_dump();
fb_queue_query_save("product", cur_product, sizeof(cur_product));
- zdata = load_file(fn, &zsize);
- if (zdata == 0) die("failed to load '%s': %s", fn, strerror(errno));
-
- zip = init_zipfile(zdata, zsize);
- if(zip == 0) die("failed to access zipdata in '%s'");
-
- data = unzip_file(zip, "android-info.txt", &sz);
- if (data == 0) {
- char *tmp;
- /* fallback for older zipfiles */
- data = unzip_file(zip, "android-product.txt", &sz);
- if ((data == 0) || (sz < 1)) {
- die("update package has no android-info.txt or android-product.txt");
- }
- tmp = malloc(sz + 128);
- if (tmp == 0) die("out of memory");
- sprintf(tmp,"board=%sversion-baseband=0.66.04.19\n",(char*)data);
- data = tmp;
- sz = strlen(tmp);
+ ZipArchiveHandle zip;
+ int error = OpenArchive(filename, &zip);
+ if (error != 0) {
+ die("failed to open zip file '%s': %s", filename, ErrorCodeString(error));
}
- setup_requirements(data, sz);
+ unsigned sz;
+ void* data = unzip_file(zip, "android-info.txt", &sz);
+ if (data == 0) {
+ die("update package '%s' has no android-info.txt", filename);
+ }
- for (i = 0; i < ARRAY_SIZE(images); i++) {
- fd = unzip_to_file(zip, images[i].img_name);
+ setup_requirements(reinterpret_cast<char*>(data), sz);
+
+ for (size_t i = 0; i < ARRAY_SIZE(images); i++) {
+ int fd = unzip_to_file(zip, images[i].img_name);
if (fd < 0) {
if (images[i].is_optional)
continue;
die("update package missing %s", images[i].img_name);
}
- rc = load_buf_fd(usb, fd, &buf);
+ fastboot_buffer buf;
+ int rc = load_buf_fd(usb, fd, &buf);
if (rc) die("cannot load %s from flash", images[i].img_name);
do_update_signature(zip, images[i].sig_name);
if (erase_first && needs_erase(images[i].part_name)) {
@@ -778,6 +742,8 @@
* program exits.
*/
}
+
+ CloseArchive(zip);
}
void do_send_signature(char *fn)
@@ -800,24 +766,22 @@
void do_flashall(usb_handle *usb, int erase_first)
{
- char *fname;
- void *data;
- unsigned sz;
- struct fastboot_buffer buf;
- size_t i;
-
queue_info_dump();
fb_queue_query_save("product", cur_product, sizeof(cur_product));
- fname = find_item("info", product);
+ char* fname = find_item("info", product);
if (fname == 0) die("cannot find android-info.txt");
- data = load_file(fname, &sz);
- if (data == 0) die("could not load android-info.txt: %s", strerror(errno));
- setup_requirements(data, sz);
- for (i = 0; i < ARRAY_SIZE(images); i++) {
+ unsigned sz;
+ void* data = load_file(fname, &sz);
+ if (data == 0) die("could not load android-info.txt: %s", strerror(errno));
+
+ setup_requirements(reinterpret_cast<char*>(data), sz);
+
+ for (size_t i = 0; i < ARRAY_SIZE(images); i++) {
fname = find_item(images[i].part_name, product);
+ fastboot_buffer buf;
if (load_buf(usb, fname, &buf)) {
if (images[i].is_optional)
continue;
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index fc5d4f4..1786e49 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -31,6 +31,10 @@
#include "usb.h"
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
struct sparse_file;
/* protocol.c - fastboot protocol */
@@ -67,7 +71,13 @@
char *mkmsg(const char *fmt, ...);
void die(const char *fmt, ...);
+void get_my_path(char *path);
+
/* Current product */
extern char cur_product[FB_RESPONSE_SZ + 1];
+#if defined(__cplusplus)
+}
+#endif
+
#endif
diff --git a/fastboot/fs.h b/fastboot/fs.h
index 8444081..307772b 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -3,10 +3,18 @@
#include <stdint.h>
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
struct fs_generator;
const struct fs_generator* fs_get_generator(const char *fs_type);
int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize);
+#if defined(__cplusplus)
+}
+#endif
+
#endif
diff --git a/fastboot/p12topem.sh b/fastboot/p12topem.sh
deleted file mode 100755
index f081eb5..0000000
--- a/fastboot/p12topem.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-
-if [ $# -ne 2 ]
-then
- echo "Usage: $0 alias passphrase"
- exit -1
-fi
-
-openssl pkcs12 -passin pass:"$2" -passout pass:"$2" -in $1.p12 -out $1.pem
diff --git a/fastboot/protocol.c b/fastboot/protocol.c
index 10a84c1..5b97600 100644
--- a/fastboot/protocol.c
+++ b/fastboot/protocol.c
@@ -305,7 +305,10 @@
return -1;
}
- fb_download_data_sparse_flush(usb);
+ r = fb_download_data_sparse_flush(usb);
+ if (r < 0) {
+ return -1;
+ }
return _command_end(usb);
}
diff --git a/fastboot/signfile.sh b/fastboot/signfile.sh
deleted file mode 100755
index 3188d2d..0000000
--- a/fastboot/signfile.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-if [ $# -ne 3 ]
-then
- echo "Usage: $0 alias filename passpharse"
- exit -1
-fi
-
-openssl dgst -passin pass:"$3" -binary -sha1 -sign $1.pem $2 > $2.sign
-
diff --git a/fastboot/usb.h b/fastboot/usb.h
index 17cf0a9..c7b748e 100644
--- a/fastboot/usb.h
+++ b/fastboot/usb.h
@@ -29,6 +29,10 @@
#ifndef _USB_H_
#define _USB_H_
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
typedef struct usb_handle usb_handle;
typedef struct usb_ifc_info usb_ifc_info;
@@ -64,4 +68,8 @@
int usb_write(usb_handle *h, const void *_data, int len);
int usb_wait_for_disconnect(usb_handle *h);
+#if defined(__cplusplus)
+}
+#endif
+
#endif
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
index 61bf1ee..08d0671 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -8,8 +8,8 @@
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_MODULE:= libfs_mgr
-LOCAL_STATIC_LIBRARIES := liblogwrap libmincrypt libext4_utils_static
-LOCAL_C_INCLUDES += system/extras/ext4_utils
+LOCAL_STATIC_LIBRARIES := liblogwrap libmincrypt libext4_utils_static libsquashfs_utils
+LOCAL_C_INCLUDES += system/extras/ext4_utils system/extras/squashfs_utils
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_CFLAGS := -Werror
@@ -34,7 +34,8 @@
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
+LOCAL_STATIC_LIBRARIES := libfs_mgr liblogwrap libcutils liblog libc libmincrypt libext4_utils_static libsquashfs_utils
+LOCAL_CXX_STL := libc++_static
LOCAL_CFLAGS := -Werror
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index d4daed6..d5e94ac 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -31,6 +31,7 @@
#include <dirent.h>
#include <ext4.h>
#include <ext4_sb.h>
+#include <ext4_crypt_init_extensions.h>
#include <linux/loop.h>
#include <private/android_filesystem_config.h>
@@ -116,8 +117,10 @@
* filesytsem due to an error, e2fsck is still run to do a full check
* fix the filesystem.
*/
+ errno = 0;
ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
- INFO("%s(): mount(%s,%s,%s)=%d\n", __func__, blk_device, target, fs_type, ret);
+ INFO("%s(): mount(%s,%s,%s)=%d: %s\n",
+ __func__, blk_device, target, fs_type, ret, strerror(errno));
if (!ret) {
int i;
for (i = 0; i < 5; i++) {
@@ -125,6 +128,7 @@
// Should we try rebooting if all attempts fail?
int result = umount(target);
if (result == 0) {
+ INFO("%s(): unmount(%s) succeeded\n", __func__, target);
break;
}
ERROR("%s(): umount(%s)=%d: %s\n", __func__, target, result, strerror(errno));
@@ -428,6 +432,63 @@
return ret;
}
+// Check to see if a mountable volume has encryption requirements
+static int handle_encryptable(struct fstab *fstab, const struct fstab_rec* rec)
+{
+ /* If this is block encryptable, need to trigger encryption */
+ if ( (rec->fs_mgr_flags & MF_FORCECRYPT)
+ || (device_is_force_encrypted() && fs_mgr_is_encryptable(rec))) {
+ if (umount(rec->mount_point) == 0) {
+ return FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;
+ } else {
+ WARNING("Could not umount %s (%s) - allow continue unencrypted\n",
+ rec->mount_point, strerror(errno));
+ return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
+ }
+ }
+
+ // Deal with file level encryption
+ if (rec->fs_mgr_flags & MF_FILEENCRYPTION) {
+ // Default or not yet initialized encryption requires no more work here
+ if (!e4crypt_non_default_key(rec->mount_point)) {
+ INFO("%s is default file encrypted\n", rec->mount_point);
+ return FS_MGR_MNTALL_DEV_DEFAULT_FILE_ENCRYPTED;
+ }
+
+ INFO("%s is non-default file encrypted\n", rec->mount_point);
+
+ // Uses non-default key, so must unmount and set up temp file system
+ if (umount(rec->mount_point)) {
+ ERROR("Failed to umount %s - rebooting\n", rec->mount_point);
+ return FS_MGR_MNTALL_FAIL;
+ }
+
+ if (fs_mgr_do_tmpfs_mount(rec->mount_point) != 0) {
+ ERROR("Failed to mount a tmpfs at %s\n", rec->mount_point);
+ return FS_MGR_MNTALL_FAIL;
+ }
+
+ // Mount data temporarily so we can access unencrypted dir
+ char tmp_mnt[PATH_MAX];
+ strlcpy(tmp_mnt, rec->mount_point, sizeof(tmp_mnt));
+ strlcat(tmp_mnt, "/tmp_mnt", sizeof(tmp_mnt));
+ if (mkdir(tmp_mnt, 0700)) {
+ ERROR("Failed to create temp mount point\n");
+ return FS_MGR_MNTALL_FAIL;
+ }
+
+ if (fs_mgr_do_mount(fstab, rec->mount_point,
+ rec->blk_device, tmp_mnt)) {
+ ERROR("Error temp mounting encrypted file system\n");
+ return FS_MGR_MNTALL_FAIL;
+ }
+
+ return FS_MGR_MNTALL_DEV_NON_DEFAULT_FILE_ENCRYPTED;
+ }
+
+ return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
+}
+
/* When multiple fstab records share the same mount_point, it will
* try to mount each one in turn, and ignore any duplicates after a
* first successful mount.
@@ -490,25 +551,21 @@
/* Deal with encryptability. */
if (!mret) {
- /* If this is encryptable, need to trigger encryption */
- if ( (fstab->recs[attempted_idx].fs_mgr_flags & MF_FORCECRYPT)
- || (device_is_force_encrypted()
- && fs_mgr_is_encryptable(&fstab->recs[attempted_idx]))) {
- if (umount(fstab->recs[attempted_idx].mount_point) == 0) {
- if (encryptable == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
- ERROR("Will try to encrypt %s %s\n", fstab->recs[attempted_idx].mount_point,
- fstab->recs[attempted_idx].fs_type);
- encryptable = FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;
- } else {
- ERROR("Only one encryptable/encrypted partition supported\n");
- encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
- }
- } else {
- WARNING("Could not umount %s (%s) - allow continue unencrypted\n",
- fstab->recs[attempted_idx].mount_point, strerror(errno));
- continue;
- }
+ int status = handle_encryptable(fstab, &fstab->recs[attempted_idx]);
+
+ if (status == FS_MGR_MNTALL_FAIL) {
+ /* Fatal error - no point continuing */
+ return status;
}
+
+ if (status != FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
+ if (encryptable != FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
+ // Log and continue
+ ERROR("Only one encryptable/encrypted partition supported\n");
+ }
+ encryptable = status;
+ }
+
/* Success! Go get the next one */
continue;
}
diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c
index ab8f128..8b0f714 100644
--- a/fs_mgr/fs_mgr_fstab.c
+++ b/fs_mgr/fs_mgr_fstab.c
@@ -24,6 +24,7 @@
struct fs_mgr_flag_values {
char *key_loc;
+ char *verity_loc;
long long part_length;
char *label;
int partnum;
@@ -60,6 +61,7 @@
{ "check", MF_CHECK },
{ "encryptable=",MF_CRYPT },
{ "forceencrypt=",MF_FORCECRYPT },
+ { "fileencryption",MF_FILEENCRYPTION },
{ "nonremovable",MF_NONREMOVABLE },
{ "voldmanaged=",MF_VOLDMANAGED},
{ "length=", MF_LENGTH },
@@ -107,6 +109,14 @@
* location of the keys. Get it and return it.
*/
flag_vals->key_loc = strdup(strchr(p, '=') + 1);
+ } else if ((fl[i].flag == MF_VERIFY) && flag_vals) {
+ /* If the verify flag is followed by an = and the
+ * location for the verity state, get it and return it.
+ */
+ char *start = strchr(p, '=');
+ if (start) {
+ flag_vals->verity_loc = strdup(start + 1);
+ }
} else if ((fl[i].flag == MF_FORCECRYPT) && flag_vals) {
/* The forceencrypt flag is followed by an = and the
* location of the keys. Get it and return it.
@@ -291,6 +301,7 @@
fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
&flag_vals, NULL, 0);
fstab->recs[cnt].key_loc = flag_vals.key_loc;
+ fstab->recs[cnt].verity_loc = flag_vals.verity_loc;
fstab->recs[cnt].length = flag_vals.part_length;
fstab->recs[cnt].label = flag_vals.label;
fstab->recs[cnt].partnum = flag_vals.partnum;
@@ -408,27 +419,32 @@
return fs_mgr_get_entry_for_mount_point_after(NULL, fstab, path);
}
-int fs_mgr_is_voldmanaged(struct fstab_rec *fstab)
+int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab)
{
return fstab->fs_mgr_flags & MF_VOLDMANAGED;
}
-int fs_mgr_is_nonremovable(struct fstab_rec *fstab)
+int fs_mgr_is_nonremovable(const struct fstab_rec *fstab)
{
return fstab->fs_mgr_flags & MF_NONREMOVABLE;
}
-int fs_mgr_is_verified(struct fstab_rec *fstab)
+int fs_mgr_is_verified(const struct fstab_rec *fstab)
{
return fstab->fs_mgr_flags & MF_VERIFY;
}
-int fs_mgr_is_encryptable(struct fstab_rec *fstab)
+int fs_mgr_is_encryptable(const struct fstab_rec *fstab)
{
return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT);
}
-int fs_mgr_is_noemulatedsd(struct fstab_rec *fstab)
+int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_FILEENCRYPTION;
+}
+
+int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab)
{
return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 88a1040..d56111a 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -76,6 +76,7 @@
#define MF_FORCECRYPT 0x400
#define MF_NOEMULATEDSD 0x800 /* no emulated sdcard daemon, sd card is the only
external storage */
+#define MF_FILEENCRYPTION 0x2000
#define DM_BUF_SIZE 4096
diff --git a/fs_mgr/fs_mgr_verity.c b/fs_mgr/fs_mgr_verity.c
index feb3c19..63b23b9 100644
--- a/fs_mgr/fs_mgr_verity.c
+++ b/fs_mgr/fs_mgr_verity.c
@@ -38,6 +38,7 @@
#include "mincrypt/sha256.h"
#include "ext4_sb.h"
+#include "squashfs_utils.h"
#include "fs_mgr_priv.h"
#include "fs_mgr_priv_verity.h"
@@ -47,16 +48,25 @@
#define VERITY_METADATA_SIZE 32768
#define VERITY_TABLE_RSA_KEY "/verity_key"
+#define METADATA_MAGIC 0x01564c54
+#define METADATA_TAG_MAX_LENGTH 63
+#define METADATA_EOD "eod"
+
+#define VERITY_LASTSIG_TAG "verity_lastsig"
+
+#define VERITY_STATE_TAG "verity_state"
#define VERITY_STATE_HEADER 0x83c0ae9d
#define VERITY_STATE_VERSION 1
#define VERITY_KMSG_RESTART "dm-verity device corrupted"
#define VERITY_KMSG_BUFSIZE 1024
+#define __STRINGIFY(x) #x
+#define STRINGIFY(x) __STRINGIFY(x)
+
struct verity_state {
uint32_t header;
uint32_t version;
- uint32_t size;
int32_t mode;
};
@@ -131,7 +141,19 @@
return retval;
}
-static int get_target_device_size(char *blk_device, uint64_t *device_size)
+static int squashfs_get_target_device_size(char *blk_device, uint64_t *device_size)
+{
+ struct squashfs_info sq_info;
+
+ if (squashfs_parse_sb(blk_device, &sq_info) >= 0) {
+ *device_size = sq_info.bytes_used_4K_padded;
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+static int ext4_get_target_device_size(char *blk_device, uint64_t *device_size)
{
int data_device;
struct ext4_super_block sb;
@@ -164,16 +186,38 @@
return 0;
}
-static int read_verity_metadata(char *block_device, char **signature, char **table)
+static int get_fs_size(char *fs_type, char *blk_device, uint64_t *device_size) {
+ if (!strcmp(fs_type, "ext4")) {
+ if (ext4_get_target_device_size(blk_device, device_size) < 0) {
+ ERROR("Failed to get ext4 fs size on %s.", blk_device);
+ return -1;
+ }
+ } else if (!strcmp(fs_type, "squashfs")) {
+ if (squashfs_get_target_device_size(blk_device, device_size) < 0) {
+ ERROR("Failed to get squashfs fs size on %s.", blk_device);
+ return -1;
+ }
+ } else {
+ ERROR("%s: Unsupported filesystem for verity.", fs_type);
+ return -1;
+ }
+ return 0;
+}
+
+static int read_verity_metadata(uint64_t device_size, char *block_device, char **signature,
+ char **table)
{
unsigned magic_number;
unsigned table_length;
- uint64_t device_length;
int protocol_version;
int device;
int retval = FS_MGR_SETUP_VERITY_FAIL;
- *signature = 0;
- *table = 0;
+
+ *signature = NULL;
+
+ if (table) {
+ *table = NULL;
+ }
device = TEMP_FAILURE_RETRY(open(block_device, O_RDONLY | O_CLOEXEC));
if (device == -1) {
@@ -181,12 +225,7 @@
goto out;
}
- // find the start of the verity metadata
- if (get_target_device_size(block_device, &device_length) < 0) {
- ERROR("Could not get target device size.\n");
- goto out;
- }
- if (TEMP_FAILURE_RETRY(lseek64(device, device_length, SEEK_SET)) < 0) {
+ if (TEMP_FAILURE_RETRY(lseek64(device, device_size, SEEK_SET)) < 0) {
ERROR("Could not seek to start of verity metadata block.\n");
goto out;
}
@@ -207,8 +246,7 @@
#endif
if (magic_number != VERITY_METADATA_MAGIC_NUMBER) {
- ERROR("Couldn't find verity metadata at offset %"PRIu64"!\n",
- device_length);
+ ERROR("Couldn't find verity metadata at offset %"PRIu64"!\n", device_size);
goto out;
}
@@ -234,6 +272,11 @@
goto out;
}
+ if (!table) {
+ retval = FS_MGR_SETUP_VERITY_SUCCESS;
+ goto out;
+ }
+
// get the size of the table
if (TEMP_FAILURE_RETRY(read(device, &table_length, sizeof(table_length))) !=
sizeof(table_length)) {
@@ -261,10 +304,13 @@
TEMP_FAILURE_RETRY(close(device));
if (retval != FS_MGR_SETUP_VERITY_SUCCESS) {
- free(*table);
free(*signature);
- *table = 0;
- *signature = 0;
+ *signature = NULL;
+
+ if (table) {
+ free(*table);
+ *table = NULL;
+ }
}
return retval;
@@ -309,17 +355,12 @@
return 0;
}
-static int load_verity_table(struct dm_ioctl *io, char *name, char *blockdev, int fd, char *table,
+static int load_verity_table(struct dm_ioctl *io, char *name, uint64_t device_size, int fd, char *table,
int mode)
{
char *verity_params;
char *buffer = (char*) io;
size_t bufsize;
- uint64_t device_size = 0;
-
- if (get_target_device_size(blockdev, &device_size) < 0) {
- return -1;
- }
verity_ioctl_init(io, name, DM_STATUS_TABLE_FLAG);
@@ -453,17 +494,116 @@
return 0;
}
+static int metadata_add(FILE *fp, long start, const char *tag,
+ unsigned int length, off64_t *offset)
+{
+ if (fseek(fp, start, SEEK_SET) < 0 ||
+ fprintf(fp, "%s %u\n", tag, length) < 0) {
+ return -1;
+ }
+
+ *offset = ftell(fp);
+
+ if (fseek(fp, length, SEEK_CUR) < 0 ||
+ fprintf(fp, METADATA_EOD " 0\n") < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int metadata_find(const char *fname, const char *stag,
+ unsigned int slength, off64_t *offset)
+{
+ FILE *fp = NULL;
+ char tag[METADATA_TAG_MAX_LENGTH + 1];
+ int rc = -1;
+ int n;
+ long start = 0x4000; /* skip cryptfs metadata area */
+ uint32_t magic;
+ unsigned int length = 0;
+
+ if (!fname) {
+ return -1;
+ }
+
+ fp = fopen(fname, "r+");
+
+ if (!fp) {
+ ERROR("Failed to open %s (%s)\n", fname, strerror(errno));
+ goto out;
+ }
+
+ /* check magic */
+ if (fseek(fp, start, SEEK_SET) < 0 ||
+ fread(&magic, sizeof(magic), 1, fp) != 1) {
+ ERROR("Failed to read magic from %s (%s)\n", fname, strerror(errno));
+ goto out;
+ }
+
+ if (magic != METADATA_MAGIC) {
+ magic = METADATA_MAGIC;
+
+ if (fseek(fp, start, SEEK_SET) < 0 ||
+ fwrite(&magic, sizeof(magic), 1, fp) != 1) {
+ ERROR("Failed to write magic to %s (%s)\n", fname, strerror(errno));
+ goto out;
+ }
+
+ rc = metadata_add(fp, start + sizeof(magic), stag, slength, offset);
+ if (rc < 0) {
+ ERROR("Failed to add metadata to %s: %s\n", fname, strerror(errno));
+ }
+
+ goto out;
+ }
+
+ start += sizeof(magic);
+
+ while (1) {
+ n = fscanf(fp, "%" STRINGIFY(METADATA_TAG_MAX_LENGTH) "s %u\n",
+ tag, &length);
+
+ if (n == 2 && strcmp(tag, METADATA_EOD)) {
+ /* found a tag */
+ start = ftell(fp);
+
+ if (!strcmp(tag, stag) && length == slength) {
+ *offset = start;
+ rc = 0;
+ goto out;
+ }
+
+ start += length;
+
+ if (fseek(fp, length, SEEK_CUR) < 0) {
+ ERROR("Failed to seek %s (%s)\n", fname, strerror(errno));
+ goto out;
+ }
+ } else {
+ rc = metadata_add(fp, start, stag, slength, offset);
+ if (rc < 0) {
+ ERROR("Failed to write metadata to %s: %s\n", fname,
+ strerror(errno));
+ }
+ goto out;
+ }
+ }
+
+out:
+ if (fp) {
+ fflush(fp);
+ fclose(fp);
+ }
+
+ return rc;
+}
+
static int write_verity_state(const char *fname, off64_t offset, int32_t mode)
{
int fd;
int rc = -1;
-
- struct verity_state s = {
- VERITY_STATE_HEADER,
- VERITY_STATE_VERSION,
- sizeof(struct verity_state),
- mode
- };
+ struct verity_state s = { VERITY_STATE_HEADER, VERITY_STATE_VERSION, mode };
fd = TEMP_FAILURE_RETRY(open(fname, O_WRONLY | O_SYNC | O_CLOEXEC));
@@ -472,13 +612,9 @@
goto out;
}
- if (TEMP_FAILURE_RETRY(lseek64(fd, offset, SEEK_SET)) < 0) {
- ERROR("Failed to seek %s to %" PRIu64 " (%s)\n", fname, offset, strerror(errno));
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(write(fd, &s, sizeof(s))) != sizeof(s)) {
- ERROR("Failed to write %zu bytes to %s (%s)\n", sizeof(s), fname, strerror(errno));
+ if (TEMP_FAILURE_RETRY(pwrite64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
+ ERROR("Failed to write %zu bytes to %s to offset %" PRIu64 " (%s)\n",
+ sizeof(s), fname, offset, strerror(errno));
goto out;
}
@@ -492,57 +628,12 @@
return rc;
}
-static int get_verity_state_location(char *location, off64_t *offset)
+static int read_verity_state(const char *fname, off64_t offset, int *mode)
{
- char state_off[PROPERTY_VALUE_MAX];
-
- if (property_get("ro.verity.state.location", location, NULL) <= 0) {
- return -1;
- }
-
- if (*location != '/' || access(location, R_OK | W_OK) == -1) {
- ERROR("Failed to access verity state %s (%s)\n", location, strerror(errno));
- return -1;
- }
-
- *offset = 0;
-
- if (property_get("ro.verity.state.offset", state_off, NULL) > 0) {
- *offset = strtoll(state_off, NULL, 0);
-
- if (errno == ERANGE || errno == EINVAL) {
- ERROR("Invalid value in ro.verity.state.offset (%s)\n", state_off);
- return -1;
- }
- }
-
- return 0;
-}
-
-int fs_mgr_load_verity_state(int *mode)
-{
- char fname[PROPERTY_VALUE_MAX];
int fd = -1;
int rc = -1;
- off64_t offset = 0;
struct verity_state s;
- if (get_verity_state_location(fname, &offset) < 0) {
- /* location for dm-verity state is not specified, fall back to
- * default behavior: return -EIO for corrupted blocks */
- *mode = VERITY_MODE_EIO;
- rc = 0;
- goto out;
- }
-
- if (was_verity_restart()) {
- /* device was restarted after dm-verity detected a corrupted
- * block, so switch to logging mode */
- *mode = VERITY_MODE_LOGGING;
- rc = write_verity_state(fname, offset, *mode);
- goto out;
- }
-
fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
if (fd == -1) {
@@ -550,17 +641,16 @@
goto out;
}
- if (TEMP_FAILURE_RETRY(lseek64(fd, offset, SEEK_SET)) < 0) {
- ERROR("Failed to seek %s to %" PRIu64 " (%s)\n", fname, offset, strerror(errno));
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(read(fd, &s, sizeof(s))) != sizeof(s)) {
- ERROR("Failed to read %zu bytes from %s (%s)\n", sizeof(s), fname, strerror(errno));
+ if (TEMP_FAILURE_RETRY(pread64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
+ ERROR("Failed to read %zu bytes from %s offset %" PRIu64 " (%s)\n",
+ sizeof(s), fname, offset, strerror(errno));
goto out;
}
if (s.header != VERITY_STATE_HEADER) {
+ /* space allocated, but no state written. write default state */
+ *mode = VERITY_MODE_DEFAULT;
+ rc = write_verity_state(fname, offset, *mode);
goto out;
}
@@ -569,11 +659,6 @@
goto out;
}
- if (s.size != sizeof(s)) {
- ERROR("Unexpected verity state size (%u)\n", s.size);
- goto out;
- }
-
if (s.mode < VERITY_MODE_EIO ||
s.mode > VERITY_MODE_LAST) {
ERROR("Unsupported verity mode (%u)\n", s.mode);
@@ -591,25 +676,185 @@
return rc;
}
-int fs_mgr_update_verity_state()
+static int compare_last_signature(struct fstab_rec *fstab, int *match)
+{
+ char tag[METADATA_TAG_MAX_LENGTH + 1];
+ char *signature = NULL;
+ int fd = -1;
+ int rc = -1;
+ uint8_t curr[SHA256_DIGEST_SIZE];
+ uint8_t prev[SHA256_DIGEST_SIZE];
+ off64_t offset = 0;
+ uint64_t device_size;
+
+ *match = 1;
+
+ // get verity filesystem size
+ if (get_fs_size(fstab->fs_type, fstab->blk_device, &device_size) < 0) {
+ ERROR("Failed to get filesystem size\n");
+ goto out;
+ }
+
+ if (read_verity_metadata(device_size, fstab->blk_device, &signature, NULL) < 0) {
+ ERROR("Failed to read verity signature from %s\n", fstab->mount_point);
+ goto out;
+ }
+
+ SHA256_hash(signature, RSANUMBYTES, curr);
+
+ if (snprintf(tag, sizeof(tag), VERITY_LASTSIG_TAG "_%s",
+ basename(fstab->mount_point)) >= (int)sizeof(tag)) {
+ ERROR("Metadata tag name too long for %s\n", fstab->mount_point);
+ goto out;
+ }
+
+ if (metadata_find(fstab->verity_loc, tag, SHA256_DIGEST_SIZE,
+ &offset) < 0) {
+ goto out;
+ }
+
+ fd = TEMP_FAILURE_RETRY(open(fstab->verity_loc, O_RDWR | O_SYNC | O_CLOEXEC));
+
+ if (fd == -1) {
+ ERROR("Failed to open %s: %s\n", fstab->verity_loc, strerror(errno));
+ goto out;
+ }
+
+ if (TEMP_FAILURE_RETRY(pread64(fd, prev, sizeof(prev),
+ offset)) != sizeof(prev)) {
+ ERROR("Failed to read %zu bytes from %s offset %" PRIu64 " (%s)\n",
+ sizeof(prev), fstab->verity_loc, offset, strerror(errno));
+ goto out;
+ }
+
+ *match = !memcmp(curr, prev, SHA256_DIGEST_SIZE);
+
+ if (!*match) {
+ /* update current signature hash */
+ if (TEMP_FAILURE_RETRY(pwrite64(fd, curr, sizeof(curr),
+ offset)) != sizeof(curr)) {
+ ERROR("Failed to write %zu bytes to %s offset %" PRIu64 " (%s)\n",
+ sizeof(curr), fstab->verity_loc, offset, strerror(errno));
+ goto out;
+ }
+ }
+
+ rc = 0;
+
+out:
+ free(signature);
+
+ if (fd != -1) {
+ TEMP_FAILURE_RETRY(close(fd));
+ }
+
+ return rc;
+}
+
+static int get_verity_state_offset(struct fstab_rec *fstab, off64_t *offset)
+{
+ char tag[METADATA_TAG_MAX_LENGTH + 1];
+
+ if (snprintf(tag, sizeof(tag), VERITY_STATE_TAG "_%s",
+ basename(fstab->mount_point)) >= (int)sizeof(tag)) {
+ ERROR("Metadata tag name too long for %s\n", fstab->mount_point);
+ return -1;
+ }
+
+ return metadata_find(fstab->verity_loc, tag, sizeof(struct verity_state),
+ offset);
+}
+
+static int load_verity_state(struct fstab_rec *fstab, int *mode)
+{
+ off64_t offset = 0;
+ int match = 0;
+
+ if (get_verity_state_offset(fstab, &offset) < 0) {
+ /* fall back to stateless behavior */
+ *mode = VERITY_MODE_EIO;
+ return 0;
+ }
+
+ if (was_verity_restart()) {
+ /* device was restarted after dm-verity detected a corrupted
+ * block, so switch to logging mode */
+ *mode = VERITY_MODE_LOGGING;
+ return write_verity_state(fstab->verity_loc, offset, *mode);
+ }
+
+ if (!compare_last_signature(fstab, &match) && !match) {
+ /* partition has been reflashed, reset dm-verity state */
+ *mode = VERITY_MODE_DEFAULT;
+ return write_verity_state(fstab->verity_loc, offset, *mode);
+ }
+
+ return read_verity_state(fstab->verity_loc, offset, mode);
+}
+
+int fs_mgr_load_verity_state(int *mode)
+{
+ char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
+ char propbuf[PROPERTY_VALUE_MAX];
+ int rc = -1;
+ int i;
+ int current;
+ struct fstab *fstab = NULL;
+
+ /* return the default mode, unless any of the verified partitions are in
+ * logging mode, in which case return that */
+ *mode = VERITY_MODE_DEFAULT;
+
+ property_get("ro.hardware", propbuf, "");
+ snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf);
+
+ fstab = fs_mgr_read_fstab(fstab_filename);
+
+ if (!fstab) {
+ ERROR("Failed to read %s\n", fstab_filename);
+ goto out;
+ }
+
+ for (i = 0; i < fstab->num_entries; i++) {
+ if (!fs_mgr_is_verified(&fstab->recs[i])) {
+ continue;
+ }
+
+ rc = load_verity_state(&fstab->recs[i], ¤t);
+ if (rc < 0) {
+ continue;
+ }
+
+ if (current == VERITY_MODE_LOGGING) {
+ *mode = current;
+ }
+ }
+
+ rc = 0;
+
+out:
+ if (fstab) {
+ fs_mgr_free_fstab(fstab);
+ }
+
+ return rc;
+}
+
+int fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback)
{
_Alignas(struct dm_ioctl) char buffer[DM_BUF_SIZE];
char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
char *mount_point;
char propbuf[PROPERTY_VALUE_MAX];
- char state_loc[PROPERTY_VALUE_MAX];
char *status;
int fd = -1;
int i;
+ int mode;
int rc = -1;
off64_t offset = 0;
struct dm_ioctl *io = (struct dm_ioctl *) buffer;
struct fstab *fstab = NULL;
- if (get_verity_state_location(state_loc, &offset) < 0) {
- goto out;
- }
-
fd = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
if (fd == -1) {
@@ -632,24 +877,34 @@
continue;
}
+ if (get_verity_state_offset(&fstab->recs[i], &offset) < 0 ||
+ read_verity_state(fstab->recs[i].verity_loc, offset, &mode) < 0) {
+ continue;
+ }
+
mount_point = basename(fstab->recs[i].mount_point);
verity_ioctl_init(io, mount_point, 0);
if (ioctl(fd, DM_TABLE_STATUS, io)) {
ERROR("Failed to query DM_TABLE_STATUS for %s (%s)\n", mount_point,
strerror(errno));
- goto out;
+ continue;
}
status = &buffer[io->data_start + sizeof(struct dm_target_spec)];
if (*status == 'C') {
- rc = write_verity_state(state_loc, offset, VERITY_MODE_LOGGING);
- goto out;
+ if (write_verity_state(fstab->recs[i].verity_loc, offset,
+ VERITY_MODE_LOGGING) < 0) {
+ continue;
+ }
+ }
+
+ if (callback) {
+ callback(&fstab->recs[i], mount_point, mode, *status);
}
}
- /* Don't overwrite possible previous state if there's no corruption. */
rc = 0;
out:
@@ -673,25 +928,21 @@
char *verity_blk_name = 0;
char *verity_table = 0;
char *verity_table_signature = 0;
+ uint64_t device_size = 0;
_Alignas(struct dm_ioctl) char buffer[DM_BUF_SIZE];
struct dm_ioctl *io = (struct dm_ioctl *) buffer;
char *mount_point = basename(fstab->mount_point);
- // set the dm_ioctl flags
- io->flags |= 1;
- io->target_count = 1;
-
- // check to ensure that the verity device is ext4
- // TODO: support non-ext4 filesystems
- if (strcmp(fstab->fs_type, "ext4")) {
- ERROR("Cannot verify non-ext4 device (%s)", fstab->fs_type);
+ // get verity filesystem size
+ if (get_fs_size(fstab->fs_type, fstab->blk_device, &device_size) < 0) {
return retval;
}
// read the verity block at the end of the block device
// send error code up the chain so we can detect attempts to disable verity
- retval = read_verity_metadata(fstab->blk_device,
+ retval = read_verity_metadata(device_size,
+ fstab->blk_device,
&verity_table_signature,
&verity_table);
if (retval < 0) {
@@ -725,12 +976,18 @@
goto out;
}
- if (fs_mgr_load_verity_state(&mode) < 0) {
- mode = VERITY_MODE_RESTART; /* default dm-verity mode */
+ if (load_verity_state(fstab, &mode) < 0) {
+ /* if accessing or updating the state failed, switch to the default
+ * safe mode. This makes sure the device won't end up in an endless
+ * restart loop, and no corrupted data will be exposed to userspace
+ * without a warning. */
+ mode = VERITY_MODE_EIO;
}
+ INFO("Enabling dm-verity for %s (mode %d)\n", mount_point, mode);
+
// load the verity mapping table
- if (load_verity_table(io, mount_point, fstab->blk_device, fd, verity_table,
+ if (load_verity_table(io, mount_point, device_size, fd, verity_table,
mode) < 0) {
goto out;
}
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 0437d45..c58a888 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -27,18 +27,19 @@
// turn verity off in userdebug builds.
#define VERITY_METADATA_MAGIC_DISABLE 0x46464f56 // "VOFF"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
// Verity modes
enum verity_mode {
VERITY_MODE_EIO = 0,
VERITY_MODE_LOGGING = 1,
VERITY_MODE_RESTART = 2,
- VERITY_MODE_LAST = VERITY_MODE_RESTART
+ VERITY_MODE_LAST = VERITY_MODE_RESTART,
+ VERITY_MODE_DEFAULT = VERITY_MODE_RESTART
};
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/*
* The entries must be kept in the same order as they were seen in the fstab.
* Unless explicitly requested, a lookup on mount point should always
@@ -66,13 +67,20 @@
unsigned int zram_size;
};
+// Callback function for verity status
+typedef void (*fs_mgr_verity_state_callback)(struct fstab_rec *fstab,
+ const char *mount_point, int mode, int status);
+
struct fstab *fs_mgr_read_fstab(const char *fstab_path);
void fs_mgr_free_fstab(struct fstab *fstab);
+#define FS_MGR_MNTALL_DEV_NON_DEFAULT_FILE_ENCRYPTED 5
+#define FS_MGR_MNTALL_DEV_DEFAULT_FILE_ENCRYPTED 4
#define FS_MGR_MNTALL_DEV_NEEDS_RECOVERY 3
#define FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION 2
#define FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED 1
#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTED 0
+#define FS_MGR_MNTALL_FAIL -1
int fs_mgr_mount_all(struct fstab *fstab);
#define FS_MGR_DOMNT_FAILED -1
@@ -84,16 +92,17 @@
int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc,
char *real_blk_device, int size);
int fs_mgr_load_verity_state(int *mode);
-int fs_mgr_update_verity_state();
+int fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback);
int fs_mgr_add_entry(struct fstab *fstab,
const char *mount_point, const char *fs_type,
const char *blk_device);
struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path);
-int fs_mgr_is_voldmanaged(struct fstab_rec *fstab);
-int fs_mgr_is_nonremovable(struct fstab_rec *fstab);
-int fs_mgr_is_verified(struct fstab_rec *fstab);
-int fs_mgr_is_encryptable(struct fstab_rec *fstab);
-int fs_mgr_is_noemulatedsd(struct fstab_rec *fstab);
+int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab);
+int fs_mgr_is_nonremovable(const struct fstab_rec *fstab);
+int fs_mgr_is_verified(const struct fstab_rec *fstab);
+int fs_mgr_is_encryptable(const struct fstab_rec *fstab);
+int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab);
+int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab);
int fs_mgr_swapon_all(struct fstab *fstab);
#ifdef __cplusplus
}
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 9ed5944..78f8403 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -88,7 +88,7 @@
int min_capacity;
bool level_only;
- gr_surface surface;
+ GRSurface* surface;
};
struct animation {
@@ -115,7 +115,7 @@
struct key_state keys[KEY_MAX + 1];
struct animation *batt_anim;
- gr_surface surf_unknown;
+ GRSurface* surf_unknown;
};
static struct frame batt_anim_frames[] = {
@@ -273,7 +273,7 @@
}
/* returns the last y-offset of where the surface ends */
-static int draw_surface_centered(struct charger* /*charger*/, gr_surface surface)
+static int draw_surface_centered(struct charger* /*charger*/, GRSurface* surface)
{
int w;
int h;
@@ -681,7 +681,7 @@
charger->batt_anim = &battery_animation;
- gr_surface* scale_frames;
+ GRSurface** scale_frames;
int scale_count;
ret = res_create_multi_display_surface("charger/battery_scale", &scale_count, &scale_frames);
if (ret < 0) {
diff --git a/include/backtrace/Backtrace.h b/include/backtrace/Backtrace.h
index 8c39acb..290682a 100644
--- a/include/backtrace/Backtrace.h
+++ b/include/backtrace/Backtrace.h
@@ -44,9 +44,6 @@
uintptr_t func_offset; // pc relative to the start of the function, only valid if func_name is not NULL.
};
-// Forward declarations.
-class BacktraceImpl;
-
#if defined(__APPLE__)
struct __darwin_ucontext;
typedef __darwin_ucontext ucontext_t;
@@ -72,7 +69,7 @@
virtual ~Backtrace();
// Get the current stack trace and store in the backtrace_ structure.
- virtual bool Unwind(size_t num_ignore_frames, ucontext_t* context = NULL);
+ virtual bool Unwind(size_t num_ignore_frames, ucontext_t* context = NULL) = 0;
// Get the function name and offset into the function given the pc.
// If the string is empty, then no valid function name was found.
@@ -95,9 +92,9 @@
virtual std::string FormatFrameData(size_t frame_num);
virtual std::string FormatFrameData(const backtrace_frame_data_t* frame);
- pid_t Pid() { return pid_; }
- pid_t Tid() { return tid_; }
- size_t NumFrames() { return frames_.size(); }
+ pid_t Pid() const { return pid_; }
+ pid_t Tid() const { return tid_; }
+ size_t NumFrames() const { return frames_.size(); }
const backtrace_frame_data_t* GetFrame(size_t frame_num) {
if (frame_num >= frames_.size()) {
@@ -117,7 +114,11 @@
BacktraceMap* GetMap() { return map_; }
protected:
- Backtrace(BacktraceImpl* impl, pid_t pid, BacktraceMap* map);
+ Backtrace(pid_t pid, pid_t tid, BacktraceMap* map);
+
+ // The name returned is not demangled, GetFunctionName() takes care of
+ // demangling the name.
+ virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) = 0;
virtual bool VerifyReadWordArgs(uintptr_t ptr, word_t* out_value);
@@ -130,10 +131,6 @@
bool map_shared_;
std::vector<backtrace_frame_data_t> frames_;
-
- BacktraceImpl* impl_;
-
- friend class BacktraceImpl;
};
#endif // _BACKTRACE_BACKTRACE_H
diff --git a/include/backtrace/BacktraceMap.h b/include/backtrace/BacktraceMap.h
index da96307..784bc03 100644
--- a/include/backtrace/BacktraceMap.h
+++ b/include/backtrace/BacktraceMap.h
@@ -37,6 +37,8 @@
uintptr_t start;
uintptr_t end;
+ uintptr_t offset;
+ uintptr_t load_base;
int flags;
std::string name;
};
@@ -82,6 +84,14 @@
return map.end > 0;
}
+ static uintptr_t GetRelativePc(const backtrace_map_t& map, uintptr_t pc) {
+ if (IsValid(map)) {
+ return pc - map.start + map.load_base;
+ } else {
+ return pc;
+ }
+ }
+
protected:
BacktraceMap(pid_t pid);
diff --git a/include/cutils/dir_hash.h b/include/cutils/dir_hash.h
deleted file mode 100644
index fbb4d02..0000000
--- a/include/cutils/dir_hash.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-typedef enum {
- SHA_1,
-} HashAlgorithm;
-
-int get_file_hash(HashAlgorithm algorithm, const char *path,
- char *output_string, size_t max_output_string);
-
-int get_recursive_hash_manifest(HashAlgorithm algorithm,
- const char *directory_path,
- char **output_string);
diff --git a/include/cutils/klog.h b/include/cutils/klog.h
index d5ae6d7..295d62b 100644
--- a/include/cutils/klog.h
+++ b/include/cutils/klog.h
@@ -18,6 +18,7 @@
#define _CUTILS_KLOG_H_
#include <sys/cdefs.h>
+#include <sys/uio.h>
#include <stdarg.h>
__BEGIN_DECLS
@@ -26,9 +27,10 @@
int klog_get_level(void);
void klog_set_level(int level);
/* TODO: void klog_close(void); - and make klog_fd users thread safe. */
+
void klog_write(int level, const char *fmt, ...)
__attribute__ ((format(printf, 2, 3)));
-void klog_vwrite(int level, const char *fmt, va_list ap);
+void klog_writev(int level, const struct iovec* iov, int iov_count);
__END_DECLS
diff --git a/include/cutils/memory.h b/include/cutils/memory.h
index e725cdd..4d26882 100644
--- a/include/cutils/memory.h
+++ b/include/cutils/memory.h
@@ -30,7 +30,7 @@
/* size is given in bytes and must be multiple of 4 */
void android_memset32(uint32_t* dst, uint32_t value, size_t size);
-#if !HAVE_STRLCPY
+#if defined(__GLIBC__) || defined(_WIN32)
/* Declaration of strlcpy() for platforms that don't already have it. */
size_t strlcpy(char *dst, const char *src, size_t size);
#endif
diff --git a/include/cutils/partition_utils.h b/include/cutils/partition_utils.h
index 597df92..72ca80d 100644
--- a/include/cutils/partition_utils.h
+++ b/include/cutils/partition_utils.h
@@ -20,7 +20,6 @@
__BEGIN_DECLS
int partition_wiped(char *source);
-void erase_footer(const char *dev_path, long long size);
__END_DECLS
diff --git a/include/cutils/sockets.h b/include/cutils/sockets.h
index c47588c..f8076ca 100644
--- a/include/cutils/sockets.h
+++ b/include/cutils/sockets.h
@@ -18,6 +18,7 @@
#define __CUTILS_SOCKETS_H
#include <errno.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
@@ -46,30 +47,19 @@
*/
static inline int android_get_control_socket(const char *name)
{
- char key[64] = ANDROID_SOCKET_ENV_PREFIX;
- const char *val;
- int fd;
+ char key[64];
+ snprintf(key, sizeof(key), ANDROID_SOCKET_ENV_PREFIX "%s", name);
- /* build our environment variable, counting cycles like a wolf ... */
-#if HAVE_STRLCPY
- strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
- name,
- sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
-#else /* for the host, which may lack the almightly strncpy ... */
- strncpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
- name,
- sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
- key[sizeof(key)-1] = '\0';
-#endif
-
- val = getenv(key);
- if (!val)
+ const char* val = getenv(key);
+ if (!val) {
return -1;
+ }
errno = 0;
- fd = strtol(val, NULL, 10);
- if (errno)
+ int fd = strtol(val, NULL, 10);
+ if (errno) {
return -1;
+ }
return fd;
}
diff --git a/include/cutils/str_parms.h b/include/cutils/str_parms.h
index 66f3637..aa1435a 100644
--- a/include/cutils/str_parms.h
+++ b/include/cutils/str_parms.h
@@ -18,6 +18,9 @@
#define __CUTILS_STR_PARMS_H
#include <stdint.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
struct str_parms;
@@ -52,4 +55,6 @@
/* debug */
void str_parms_dump(struct str_parms *str_parms);
+__END_DECLS
+
#endif /* __CUTILS_STR_PARMS_H */
diff --git a/include/cutils/threads.h b/include/cutils/threads.h
index ade9a0c..5727494 100644
--- a/include/cutils/threads.h
+++ b/include/cutils/threads.h
@@ -17,6 +17,14 @@
#ifndef _LIBS_CUTILS_THREADS_H
#define _LIBS_CUTILS_THREADS_H
+#include <sys/types.h>
+
+#if !defined(_WIN32)
+#include <pthread.h>
+#else
+#include <windows.h>
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -29,29 +37,25 @@
/***********************************************************************/
/***********************************************************************/
-#if !defined(_WIN32)
+extern pid_t gettid();
-#include <pthread.h>
+#if !defined(_WIN32)
typedef struct {
pthread_mutex_t lock;
int has_tls;
pthread_key_t tls;
-
} thread_store_t;
#define THREAD_STORE_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0, 0 }
#else // !defined(_WIN32)
-#include <windows.h>
-
typedef struct {
int lock_init;
int has_tls;
DWORD tls;
CRITICAL_SECTION lock;
-
} thread_store_t;
#define THREAD_STORE_INITIALIZER { 0, 0, 0, {0, 0, 0, 0, 0, 0} }
diff --git a/include/cutils/trace.h b/include/cutils/trace.h
index 59ff6c1..e4ed179 100644
--- a/include/cutils/trace.h
+++ b/include/cutils/trace.h
@@ -18,6 +18,7 @@
#define _LIBS_CUTILS_TRACE_H
#include <inttypes.h>
+#include <stdatomic.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
@@ -25,7 +26,6 @@
#include <sys/types.h>
#include <unistd.h>
-#include <cutils/atomic.h>
#include <cutils/compiler.h>
__BEGIN_DECLS
@@ -80,7 +80,6 @@
#error ATRACE_TAG must be defined to be one of the tags defined in cutils/trace.h
#endif
-#ifdef HAVE_ANDROID_OS
/**
* Opens the trace file for writing and reads the property for initial tags.
* The atrace.tags.enableflags property sets the tags to trace.
@@ -114,7 +113,7 @@
* Nonzero indicates setup has completed.
* Note: This does NOT indicate whether or not setup was successful.
*/
-extern volatile int32_t atrace_is_ready;
+extern atomic_bool atrace_is_ready;
/**
* Set of ATRACE_TAG flags to trace for, initialized to ATRACE_TAG_NOT_READY.
@@ -137,7 +136,7 @@
#define ATRACE_INIT() atrace_init()
static inline void atrace_init()
{
- if (CC_UNLIKELY(!android_atomic_acquire_load(&atrace_is_ready))) {
+ if (CC_UNLIKELY(!atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
atrace_setup();
}
}
@@ -248,19 +247,6 @@
}
}
-#else // not HAVE_ANDROID_OS
-
-#define ATRACE_INIT()
-#define ATRACE_GET_ENABLED_TAGS()
-#define ATRACE_ENABLED() 0
-#define ATRACE_BEGIN(name)
-#define ATRACE_END()
-#define ATRACE_ASYNC_BEGIN(name, cookie)
-#define ATRACE_ASYNC_END(name, cookie)
-#define ATRACE_INT(name, value)
-
-#endif // not HAVE_ANDROID_OS
-
__END_DECLS
#endif // _LIBS_CUTILS_TRACE_H
diff --git a/include/log/log.h b/include/log/log.h
index 99015db..0b17574 100644
--- a/include/log/log.h
+++ b/include/log/log.h
@@ -67,6 +67,23 @@
// ---------------------------------------------------------------------
+#ifndef __predict_false
+#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
+#endif
+
+/*
+ * -DLINT_RLOG in sources that you want to enforce that all logging
+ * goes to the radio log buffer. If any logging goes to any of the other
+ * log buffers, there will be a compile or link error to highlight the
+ * problem. This is not a replacement for a full audit of the code since
+ * this only catches compiled code, not ifdef'd debug code. Options to
+ * defining this, either temporarily to do a spot check, or permanently
+ * to enforce, in all the communications trees; We have hopes to ensure
+ * that by supplying just the radio log buffer that the communications
+ * teams will have their one-stop shop for triaging issues.
+ */
+#ifndef LINT_RLOG
+
/*
* Simplified macro to send a verbose log message using the current LOG_TAG.
*/
@@ -79,10 +96,6 @@
#endif
#endif
-#ifndef __predict_false
-#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
-#endif
-
#ifndef ALOGV_IF
#if LOG_NDEBUG
#define ALOGV_IF(cond, ...) ((void)0)
@@ -283,6 +296,8 @@
: (void)0 )
#endif
+#endif /* !LINT_RLOG */
+
// ---------------------------------------------------------------------
/*
@@ -477,6 +492,7 @@
EVENT_TYPE_LONG = 1,
EVENT_TYPE_STRING = 2,
EVENT_TYPE_LIST = 3,
+ EVENT_TYPE_FLOAT = 4,
} AndroidEventLogType;
#define sizeof_AndroidEventLogType sizeof(typeof_AndroidEventLogType)
#define typeof_AndroidEventLogType unsigned char
@@ -495,6 +511,13 @@
sizeof(longBuf)); \
}
#endif
+#ifndef LOG_EVENT_FLOAT
+#define LOG_EVENT_FLOAT(_tag, _value) { \
+ float floatBuf = _value; \
+ (void) android_btWriteLog(_tag, EVENT_TYPE_FLOAT, &floatBuf, \
+ sizeof(floatBuf)); \
+ }
+#endif
#ifndef LOG_EVENT_STRING
#define LOG_EVENT_STRING(_tag, _value) \
(void) __android_log_bswrite(_tag, _value);
@@ -567,11 +590,16 @@
typedef enum log_id {
LOG_ID_MIN = 0,
+#ifndef LINT_RLOG
LOG_ID_MAIN = 0,
+#endif
LOG_ID_RADIO = 1,
+#ifndef LINT_RLOG
LOG_ID_EVENTS = 2,
LOG_ID_SYSTEM = 3,
LOG_ID_CRASH = 4,
+ LOG_ID_KERNEL = 5,
+#endif
LOG_ID_MAX
} log_id_t;
diff --git a/include/log/logprint.h b/include/log/logprint.h
index 1e42b47..96249e9 100644
--- a/include/log/logprint.h
+++ b/include/log/logprint.h
@@ -36,7 +36,9 @@
FORMAT_TIME,
FORMAT_THREADTIME,
FORMAT_LONG,
- FORMAT_COLOR,
+ /* The following two are modifiers to above formats */
+ FORMAT_MODIFIER_COLOR, /* converts priority to color */
+ FORMAT_MODIFIER_TIME_USEC, /* switches from msec to usec time precision */
} AndroidLogPrintFormat;
typedef struct AndroidLogFormat_t AndroidLogFormat;
@@ -56,7 +58,8 @@
void android_log_format_free(AndroidLogFormat *p_format);
-void android_log_setPrintFormat(AndroidLogFormat *p_format,
+/* currently returns 0 if format is a modifier, 1 if not */
+int android_log_setPrintFormat(AndroidLogFormat *p_format,
AndroidLogPrintFormat format);
/**
@@ -64,7 +67,7 @@
*/
AndroidLogPrintFormat android_log_formatFromString(const char *s);
-/**
+/**
* filterExpression: a single filter expression
* eg "AT:d"
*
@@ -74,12 +77,12 @@
*
*/
-int android_log_addFilterRule(AndroidLogFormat *p_format,
+int android_log_addFilterRule(AndroidLogFormat *p_format,
const char *filterExpression);
-/**
- * filterString: a whitespace-separated set of filter expressions
+/**
+ * filterString: a whitespace-separated set of filter expressions
* eg "AT:d *:i"
*
* returns 0 on success and -1 on invalid expression
@@ -92,7 +95,7 @@
const char *filterString);
-/**
+/**
* returns 1 if this log line should be printed based on its priority
* and tag, and 0 if it should not
*/
@@ -129,7 +132,7 @@
* Returns NULL on malloc error
*/
-char *android_log_formatLogLine (
+char *android_log_formatLogLine (
AndroidLogFormat *p_format,
char *defaultBuffer,
size_t defaultBufferSize,
diff --git a/include/memtrack/memtrack.h b/include/memtrack/memtrack.h
index 0f1f85e..3917300 100644
--- a/include/memtrack/memtrack.h
+++ b/include/memtrack/memtrack.h
@@ -121,7 +121,7 @@
ssize_t memtrack_proc_gl_pss(struct memtrack_proc *p);
/**
- * memtrack_proc_gl_total
+ * memtrack_proc_other_total
*
* Same as memtrack_proc_graphics_total, but counts miscellaneous memory
* not tracked by gl or graphics calls above.
@@ -131,7 +131,7 @@
ssize_t memtrack_proc_other_total(struct memtrack_proc *p);
/**
- * memtrack_proc_gl_pss
+ * memtrack_proc_other_pss
*
* Same as memtrack_proc_graphics_total, but counts miscellaneous memory
* not tracked by gl or graphics calls above.
diff --git a/include/private/android_filesystem_capability.h b/include/private/android_filesystem_capability.h
index 0505cda..b92d3db 100644
--- a/include/private/android_filesystem_capability.h
+++ b/include/private/android_filesystem_capability.h
@@ -105,7 +105,9 @@
#define CAP_MAC_ADMIN 33
#define CAP_SYSLOG 34
#define CAP_WAKE_ALARM 35
-#define CAP_LAST_CAP CAP_WAKE_ALARM
+#define CAP_BLOCK_SUSPEND 36
+#define CAP_AUDIT_READ 37
+#define CAP_LAST_CAP CAP_AUDIT_READ
#define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP)
#define CAP_TO_INDEX(x) ((x) >> 5)
#define CAP_TO_MASK(x) (1 << ((x) & 31))
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index a3d11a7..02fe2b5 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -22,8 +22,7 @@
#ifndef _ANDROID_FILESYSTEM_CONFIG_H_
#define _ANDROID_FILESYSTEM_CONFIG_H_
-#include <string.h>
-#include <sys/stat.h>
+#include <sys/cdefs.h>
#include <sys/types.h>
#include <stdint.h>
@@ -114,6 +113,14 @@
#define AID_SHARED_GID_END 59999 /* start of gids for apps in each user to share */
#if !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
+/*
+ * Used in:
+ * bionic/libc/bionic/stubs.cpp
+ * external/libselinux/src/android.c
+ * system/core/logd/LogStatistics.cpp
+ * system/core/init/ueventd.cpp
+ * system/core/init/util.cpp
+ */
struct android_id_info {
const char *name;
unsigned aid;
@@ -191,116 +198,26 @@
const char *prefix;
};
-/* Rules for directories.
-** These rules are applied based on "first match", so they
-** should start with the most specific path and work their
-** way up to the root.
-*/
+/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */
-static const struct fs_path_config android_dirs[] = {
- { 00770, AID_SYSTEM, AID_CACHE, 0, "cache" },
- { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" },
- { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private" },
- { 00771, AID_ROOT, AID_ROOT, 0, "data/dalvik-cache" },
- { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/data" },
- { 00771, AID_SHELL, AID_SHELL, 0, "data/local/tmp" },
- { 00771, AID_SHELL, AID_SHELL, 0, "data/local" },
- { 01771, AID_SYSTEM, AID_MISC, 0, "data/misc" },
- { 00770, AID_DHCP, AID_DHCP, 0, "data/misc/dhcp" },
- { 00771, AID_SHARED_RELRO, AID_SHARED_RELRO, 0, "data/misc/shared_relro" },
- { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media" },
- { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/Music" },
- { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" },
- { 00750, AID_ROOT, AID_SHELL, 0, "sbin" },
- { 00755, AID_ROOT, AID_SHELL, 0, "system/bin" },
- { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor" },
- { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin" },
- { 00755, AID_ROOT, AID_ROOT, 0, "system/etc/ppp" },
- { 00755, AID_ROOT, AID_SHELL, 0, "vendor" },
- { 00777, AID_ROOT, AID_ROOT, 0, "sdcard" },
- { 00755, AID_ROOT, AID_ROOT, 0, 0 },
-};
+__BEGIN_DECLS
-/* Rules for files.
-** These rules are applied based on "first match", so they
-** should start with the most specific path and work their
-** way up to the root. Prefixes ending in * denotes wildcard
-** and will allow partial matches.
-*/
-static const struct fs_path_config android_files[] = {
- { 00440, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.rc" },
- { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.sh" },
- { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.ril" },
- { 00550, AID_DHCP, AID_SHELL, 0, "system/etc/dhcpcd/dhcpcd-run-hooks" },
- { 00555, AID_ROOT, AID_ROOT, 0, "system/etc/ppp/*" },
- { 00555, AID_ROOT, AID_ROOT, 0, "system/etc/rc.*" },
- { 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app/*" },
- { 00644, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/*" },
- { 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private/*" },
- { 00644, AID_APP, AID_APP, 0, "data/data/*" },
+/*
+ * Used in:
+ * build/tools/fs_config/fs_config.c
+ * build/tools/fs_get_stats/fs_get_stats.c
+ * external/genext2fs/genext2fs.c
+ * external/squashfs-tools/squashfs-tools/android.c
+ * system/core/cpio/mkbootfs.c
+ * system/core/adb/file_sync_service.cpp
+ * system/extras/ext4_utils/canned_fs_config.c
+ */
+void fs_config(const char *path, int dir,
+ unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities);
- /* the following five 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" },
+ssize_t fs_config_generate(char *buffer, size_t length, const struct fs_path_config *pc);
- /* the following files have enhanced capabilities and ARE included in user builds. */
- { 00750, AID_ROOT, AID_SHELL, (1 << CAP_SETUID) | (1 << CAP_SETGID), "system/bin/run-as" },
+__END_DECLS
- { 00750, AID_ROOT, AID_ROOT, 0, "system/bin/uncrypt" },
- { 00750, AID_ROOT, AID_ROOT, 0, "system/bin/install-recovery.sh" },
- { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" },
- { 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" },
- { 00755, AID_ROOT, AID_ROOT, 0, "system/lib64/valgrind/*" },
- { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" },
- { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor/bin/*" },
- { 00755, AID_ROOT, AID_SHELL, 0, "vendor/bin/*" },
- { 00750, AID_ROOT, AID_SHELL, 0, "sbin/*" },
- { 00755, AID_ROOT, AID_ROOT, 0, "bin/*" },
- { 00750, AID_ROOT, AID_SHELL, 0, "init*" },
- { 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" },
- { 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
- { 00644, AID_ROOT, AID_ROOT, 0, 0 },
-};
-
-static inline void fs_config(const char *path, int dir,
- unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities)
-{
- const struct fs_path_config *pc;
- int plen;
-
- if (path[0] == '/') {
- path++;
- }
-
- pc = dir ? android_dirs : android_files;
- plen = strlen(path);
- for(; pc->prefix; pc++){
- int len = strlen(pc->prefix);
- if (dir) {
- if(plen < len) continue;
- if(!strncmp(pc->prefix, path, len)) break;
- continue;
- }
- /* If name ends in * then allow partial matches. */
- if (pc->prefix[len -1] == '*') {
- if(!strncmp(pc->prefix, path, len - 1)) break;
- } else if (plen == len){
- if(!strncmp(pc->prefix, path, len)) break;
- }
- }
- *uid = pc->uid;
- *gid = pc->gid;
- *mode = (*mode & (~07777)) | pc->mode;
- *capabilities = pc->capabilities;
-
-#if 0
- fprintf(stderr,"< '%s' '%s' %d %d %o >\n",
- path, pc->prefix ? pc->prefix : "", *uid, *gid, *mode);
-#endif
-}
#endif
#endif
diff --git a/include/private/android_logger.h b/include/private/android_logger.h
index 724ca51..04238a6 100644
--- a/include/private/android_logger.h
+++ b/include/private/android_logger.h
@@ -70,7 +70,17 @@
android_event_long_t payload;
} android_log_event_long_t;
-/* Event payload EVENT_TYPE_STRING */
+/*
+ * Event payload EVENT_TYPE_STRING
+ *
+ * Danger: do not embed this structure into another structure.
+ * This structure uses a flexible array member, and when
+ * compiled using g++, __builtin_object_size(data, 1) returns
+ * a bad value. This is possibly a g++ bug, or a bug due to
+ * the fact that flexible array members are not supported
+ * in C++.
+ * http://stackoverflow.com/questions/4412749/are-flexible-array-members-valid-in-c
+ */
typedef struct __attribute__((__packed__)) {
int8_t type; // EVENT_TYPE_STRING;
int32_t length; // Little Endian Order
@@ -80,7 +90,9 @@
/* Event with single EVENT_TYPE_STRING */
typedef struct __attribute__((__packed__)) {
android_event_header_t header;
- android_event_string_t payload;
+ int8_t type; // EVENT_TYPE_STRING;
+ int32_t length; // Little Endian Order
+ char data[];
} android_log_event_string_t;
#endif
diff --git a/include/system/audio.h b/include/system/audio.h
index 181a171..8d9ec88 100644
--- a/include/system/audio.h
+++ b/include/system/audio.h
@@ -1102,8 +1102,10 @@
static inline bool audio_is_remote_submix_device(audio_devices_t device)
{
- if ((device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX) == AUDIO_DEVICE_OUT_REMOTE_SUBMIX
- || (device & AUDIO_DEVICE_IN_REMOTE_SUBMIX) == AUDIO_DEVICE_IN_REMOTE_SUBMIX)
+ if ((audio_is_output_devices(device) &&
+ (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX) == AUDIO_DEVICE_OUT_REMOTE_SUBMIX)
+ || (!audio_is_output_devices(device) &&
+ (device & AUDIO_DEVICE_IN_REMOTE_SUBMIX) == AUDIO_DEVICE_IN_REMOTE_SUBMIX))
return true;
else
return false;
diff --git a/include/system/graphics.h b/include/system/graphics.h
index c3fca97..efd48cb 100644
--- a/include/system/graphics.h
+++ b/include/system/graphics.h
@@ -45,9 +45,12 @@
/*
* "linear" color pixel formats:
*
- * The pixel formats below contain sRGB data but are otherwise treated
- * as linear formats, i.e.: no special operation is performed when
- * reading or writing into a buffer in one of these formats
+ * When used with ANativeWindow, the dataSpace field describes the color
+ * space of the buffer.
+ *
+ * The color space determines, for example, if the formats are linear or
+ * gamma-corrected; or whether any special operations are performed when
+ * reading or writing into a buffer in one of these formats.
*/
HAL_PIXEL_FORMAT_RGBA_8888 = 1,
HAL_PIXEL_FORMAT_RGBX_8888 = 2,
@@ -55,25 +58,8 @@
HAL_PIXEL_FORMAT_RGB_565 = 4,
HAL_PIXEL_FORMAT_BGRA_8888 = 5,
- /*
- * sRGB color pixel formats:
- *
- * The red, green and blue components are stored in sRGB space, and converted
- * to linear space when read, using the standard sRGB to linear equation:
- *
- * Clinear = Csrgb / 12.92 for Csrgb <= 0.04045
- * = (Csrgb + 0.055 / 1.055)^2.4 for Csrgb > 0.04045
- *
- * When written the inverse transformation is performed:
- *
- * Csrgb = 12.92 * Clinear for Clinear <= 0.0031308
- * = 1.055 * Clinear^(1/2.4) - 0.055 for Clinear > 0.0031308
- *
- *
- * The alpha component, if present, is always stored in linear space and
- * is left unmodified when read or written.
- *
- */
+ // Deprecated sRGB formats for source code compatibility
+ // Not for use in new code
HAL_PIXEL_FORMAT_sRGB_A_8888 = 0xC,
HAL_PIXEL_FORMAT_sRGB_X_8888 = 0xD,
@@ -111,6 +97,8 @@
* cr_offset = y_size
* cb_offset = y_size + c_size
*
+ * When used with ANativeWindow, the dataSpace field describes the color
+ * space of the buffer.
*/
HAL_PIXEL_FORMAT_YV12 = 0x32315659, // YCrCb 4:2:0 Planar
@@ -135,6 +123,8 @@
*
* size = stride * height
*
+ * When used with ANativeWindow, the dataSpace field describes the color
+ * space of the buffer.
*/
HAL_PIXEL_FORMAT_Y8 = 0x20203859,
@@ -159,6 +149,10 @@
*
* size = stride * height * 2
*
+ * When used with ANativeWindow, the dataSpace field describes the color
+ * space of the buffer, except that dataSpace field
+ * HAL_DATASPACE_DEPTH indicates that this buffer contains a depth
+ * image where each sample is a distance value measured by a depth camera.
*/
HAL_PIXEL_FORMAT_Y16 = 0x20363159,
@@ -167,7 +161,7 @@
*
* This format is exposed outside of the camera HAL to applications.
*
- * RAW_SENSOR is a single-channel, 16-bit, little endian format, typically
+ * RAW16 is a single-channel, 16-bit, little endian format, typically
* representing raw Bayer-pattern images from an image sensor, with minimal
* processing.
*
@@ -193,9 +187,15 @@
* - GRALLOC_USAGE_HW_CAMERA_*
* - GRALLOC_USAGE_SW_*
* - GRALLOC_USAGE_RENDERSCRIPT
+ *
+ * When used with ANativeWindow, the dataSpace should be
+ * HAL_DATASPACE_ARBITRARY, as raw image sensor buffers require substantial
+ * extra metadata to define.
*/
HAL_PIXEL_FORMAT_RAW16 = 0x20,
- HAL_PIXEL_FORMAT_RAW_SENSOR = 0x20, // TODO(rubenbrunk): Remove RAW_SENSOR.
+
+ // Temporary alias for source code compatibility; do not use in new code
+ HAL_PIXEL_FORMAT_RAW_SENSOR = HAL_PIXEL_FORMAT_RAW16,
/*
* Android RAW10 format:
@@ -244,6 +244,10 @@
* - GRALLOC_USAGE_HW_CAMERA_*
* - GRALLOC_USAGE_SW_*
* - GRALLOC_USAGE_RENDERSCRIPT
+ *
+ * When used with ANativeWindow, the dataSpace field should be
+ * HAL_DATASPACE_ARBITRARY, as raw image sensor buffers require substantial
+ * extra metadata to define.
*/
HAL_PIXEL_FORMAT_RAW10 = 0x25,
@@ -261,6 +265,10 @@
* - GRALLOC_USAGE_HW_CAMERA_*
* - GRALLOC_USAGE_SW_*
* - GRALLOC_USAGE_RENDERSCRIPT
+ *
+ * When used with ANativeWindow, the dataSpace field should be
+ * HAL_DATASPACE_ARBITRARY, as raw image sensor buffers require substantial
+ * extra metadata to define.
*/
HAL_PIXEL_FORMAT_RAW_OPAQUE = 0x24,
@@ -276,6 +284,16 @@
*
* Buffers of this format must have a height of 1, and width equal to their
* size in bytes.
+ *
+ * When used with ANativeWindow, the mapping of the dataSpace field to
+ * buffer contents for BLOB is as follows:
+ *
+ * dataSpace value | Buffer contents
+ * -------------------------------+-----------------------------------------
+ * HAL_DATASPACE_JFIF | An encoded JPEG image
+ * HAL_DATASPACE_DEPTH | An android_depth_points buffer
+ * Other | Unsupported
+ *
*/
HAL_PIXEL_FORMAT_BLOB = 0x21,
@@ -292,6 +310,8 @@
* framework will assume that sampling the texture will always return an
* alpha value of 1.0 (i.e. the buffer contains only opaque pixel values).
*
+ * When used with ANativeWindow, the dataSpace field describes the color
+ * space of the buffer.
*/
HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22,
@@ -311,6 +331,9 @@
*
* This format is locked for use by gralloc's (*lock_ycbcr) method, and
* locking with the (*lock) method will return an error.
+ *
+ * When used with ANativeWindow, the dataSpace field describes the color
+ * space of the buffer.
*/
HAL_PIXEL_FORMAT_YCbCr_420_888 = 0x23,
@@ -355,6 +378,42 @@
};
/**
+ * Structure used to define depth point clouds for format HAL_PIXEL_FORMAT_BLOB
+ * with dataSpace value of HAL_DATASPACE_DEPTH.
+ * When locking a native buffer of the above format and dataSpace value,
+ * the vaddr pointer can be cast to this structure.
+ *
+ * A variable-length list of (x,y,z) 3D points, as floats.
+ *
+ * @num_points is the number of points in the list
+ *
+ * @xyz_points is the flexible array of floating-point values.
+ * It contains (num_points) * 3 floats.
+ *
+ * For example:
+ * android_depth_points d = get_depth_buffer();
+ * struct {
+ * float x; float y; float z;
+ * } firstPoint, lastPoint;
+ *
+ * firstPoint.x = d.xyz_points[0];
+ * firstPoint.y = d.xyz_points[1];
+ * firstPoint.z = d.xyz_points[2];
+ * lastPoint.x = d.xyz_points[(d.num_points - 1) * 3 + 0];
+ * lastPoint.y = d.xyz_points[(d.num_points - 1) * 3 + 1];
+ * lastPoint.z = d.xyz_points[(d.num_points - 1) * 3 + 2];
+ */
+
+struct android_depth_points {
+ uint32_t num_points;
+
+ /** reserved for future use, set to 0 by gralloc's (*lock)() */
+ uint32_t reserved[8];
+
+ float xyz_points[];
+};
+
+/**
* Transformation definitions
*
* IMPORTANT NOTE:
@@ -378,19 +437,33 @@
};
/**
- * Colorspace Definitions
+ * Dataspace Definitions
* ======================
*
- * Colorspace is the definition of how pixel values should be interpreted.
- * It includes primaries (including white point) and the transfer
- * characteristic function, which describes both gamma curve and numeric
- * range (within the bit depth).
+ * Dataspace is the definition of how pixel values should be interpreted.
+ *
+ * For many formats, this is the colorspace of the image data, which includes
+ * primaries (including white point) and the transfer characteristic function,
+ * which describes both gamma curve and numeric range (within the bit depth).
+ *
+ * Other dataspaces include depth measurement data from a depth camera.
*/
-enum {
+typedef enum android_dataspace {
/*
- * Arbitrary colorspace with manually defined characteristics.
- * Colorspace definition must be communicated separately.
+ * Default-assumption data space, when not explicitly specified.
+ *
+ * It is safest to assume the buffer is an image with sRGB primaries and
+ * encoding ranges, but the consumer and/or the producer of the data may
+ * simply be using defaults. No automatic gamma transform should be
+ * expected, except for a possible display gamma transform when drawn to a
+ * screen.
+ */
+ HAL_DATASPACE_UNKNOWN = 0x0,
+
+ /*
+ * Arbitrary dataspace with manually defined characteristics. Definition
+ * for colorspaces or other meaning must be communicated separately.
*
* This is used when specifying primaries, transfer characteristics,
* etc. separately.
@@ -399,7 +472,57 @@
* where a colorspace can have separately defined primaries, transfer
* characteristics, etc.
*/
- HAL_COLORSPACE_ARBITRARY = 0x1,
+ HAL_DATASPACE_ARBITRARY = 0x1,
+
+ /*
+ * RGB Colorspaces
+ * -----------------
+ *
+ * Primaries are given using (x,y) coordinates in the CIE 1931 definition
+ * of x and y specified by ISO 11664-1.
+ *
+ * Transfer characteristics are the opto-electronic transfer characteristic
+ * at the source as a function of linear optical intensity (luminance).
+ */
+
+ /*
+ * sRGB linear encoding:
+ *
+ * The red, green, and blue components are stored in sRGB space, but
+ * are linear, not gamma-encoded.
+ * The RGB primaries and the white point are the same as BT.709.
+ *
+ * The values are encoded using the full range ([0,255] for 8-bit) for all
+ * components.
+ */
+ HAL_DATASPACE_SRGB_LINEAR = 0x200,
+
+ /*
+ * sRGB gamma encoding:
+ *
+ * The red, green and blue components are stored in sRGB space, and
+ * converted to linear space when read, using the standard sRGB to linear
+ * equation:
+ *
+ * Clinear = Csrgb / 12.92 for Csrgb <= 0.04045
+ * = (Csrgb + 0.055 / 1.055)^2.4 for Csrgb > 0.04045
+ *
+ * When written the inverse transformation is performed:
+ *
+ * Csrgb = 12.92 * Clinear for Clinear <= 0.0031308
+ * = 1.055 * Clinear^(1/2.4) - 0.055 for Clinear > 0.0031308
+ *
+ *
+ * The alpha component, if present, is always stored in linear space and
+ * is left unmodified when read or written.
+ *
+ * The RGB primaries and the white point are the same as BT.709.
+ *
+ * The values are encoded using the full range ([0,255] for 8-bit) for all
+ * components.
+ *
+ */
+ HAL_DATASPACE_SRGB = 0x201,
/*
* YCbCr Colorspaces
@@ -429,7 +552,7 @@
* red 0.640 0.330
* white (D65) 0.3127 0.3290
*/
- HAL_COLORSPACE_JFIF = 0x101,
+ HAL_DATASPACE_JFIF = 0x101,
/*
* ITU-R Recommendation 601 (BT.601) - 625-line
@@ -456,7 +579,7 @@
* red 0.640 0.330
* white (D65) 0.3127 0.3290
*/
- HAL_COLORSPACE_BT601_625 = 0x102,
+ HAL_DATASPACE_BT601_625 = 0x102,
/*
* ITU-R Recommendation 601 (BT.601) - 525-line
@@ -483,7 +606,7 @@
* red 0.630 0.340
* white (D65) 0.3127 0.3290
*/
- HAL_COLORSPACE_BT601_525 = 0x103,
+ HAL_DATASPACE_BT601_525 = 0x103,
/*
* ITU-R Recommendation 709 (BT.709)
@@ -504,8 +627,20 @@
* red 0.640 0.330
* white (D65) 0.3127 0.3290
*/
- HAL_COLORSPACE_BT709 = 0x104,
-};
+ HAL_DATASPACE_BT709 = 0x104,
+
+ /*
+ * The buffer contains depth ranging measurements from a depth camera.
+ * This value is valid with formats:
+ * HAL_PIXEL_FORMAT_Y16: 16-bit single channel depth image.
+ * HAL_PIXEL_FORMAT_BLOB: A depth point cloud, as
+ * a variable-length float (x,y,z) coordinate point list.
+ * The point cloud will be represented with the android_depth_points
+ * structure.
+ */
+ HAL_DATASPACE_DEPTH = 0x1000
+
+} android_dataspace_t;
#ifdef __cplusplus
}
diff --git a/include/system/window.h b/include/system/window.h
index bf93b79..508ce00 100644
--- a/include/system/window.h
+++ b/include/system/window.h
@@ -262,6 +262,21 @@
* the aspect ratio of the buffers produced.
*/
NATIVE_WINDOW_STICKY_TRANSFORM = 11,
+
+ /**
+ * The default data space for the buffers as set by the consumer.
+ * The values are defined in graphics.h.
+ */
+ NATIVE_WINDOW_DEFAULT_DATASPACE = 12,
+
+ /*
+ * Returns the age of the contents of the most recently dequeued buffer as
+ * the number of frames that have elapsed since it was last queued. For
+ * example, if the window is double-buffered, the age of any given buffer in
+ * steady state will be 2. If the dequeued buffer has never been queued, its
+ * age will be 0.
+ */
+ NATIVE_WINDOW_BUFFER_AGE = 13,
};
/* Valid operations for the (*perform)() hook.
@@ -294,6 +309,8 @@
NATIVE_WINDOW_SET_POST_TRANSFORM_CROP = 16, /* private */
NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM = 17,/* private */
NATIVE_WINDOW_SET_SIDEBAND_STREAM = 18,
+ NATIVE_WINDOW_SET_BUFFERS_DATASPACE = 19,
+ NATIVE_WINDOW_SET_SURFACE_DAMAGE = 20, /* private */
};
/* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */
@@ -486,30 +503,12 @@
* DO NOT CALL THIS HOOK DIRECTLY. Instead, use the helper functions
* defined below.
*
- * (*perform)() returns -ENOENT if the 'what' parameter is not supported
- * by the surface's implementation.
+ * (*perform)() returns -ENOENT if the 'what' parameter is not supported
+ * by the surface's implementation.
*
- * The valid operations are:
- * NATIVE_WINDOW_SET_USAGE
- * NATIVE_WINDOW_CONNECT (deprecated)
- * NATIVE_WINDOW_DISCONNECT (deprecated)
- * NATIVE_WINDOW_SET_CROP (private)
- * NATIVE_WINDOW_SET_BUFFER_COUNT
- * NATIVE_WINDOW_SET_BUFFERS_GEOMETRY (deprecated)
- * NATIVE_WINDOW_SET_BUFFERS_TRANSFORM
- * NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP
- * NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS
- * NATIVE_WINDOW_SET_BUFFERS_FORMAT
- * NATIVE_WINDOW_SET_SCALING_MODE (private)
- * NATIVE_WINDOW_LOCK (private)
- * NATIVE_WINDOW_UNLOCK_AND_POST (private)
- * NATIVE_WINDOW_API_CONNECT (private)
- * NATIVE_WINDOW_API_DISCONNECT (private)
- * NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS (private)
- * NATIVE_WINDOW_SET_POST_TRANSFORM_CROP (private)
- *
+ * See above for a list of valid operations, such as
+ * NATIVE_WINDOW_SET_USAGE or NATIVE_WINDOW_CONNECT
*/
-
int (*perform)(struct ANativeWindow* window,
int operation, ... );
@@ -799,6 +798,26 @@
}
/*
+ * native_window_set_buffers_data_space(..., int dataSpace)
+ * All buffers queued after this call will be associated with the dataSpace
+ * parameter specified.
+ *
+ * dataSpace specifies additional information about the buffer that's dependent
+ * on the buffer format and the endpoints. For example, it can be used to convey
+ * the color space of the image data in the buffer, or it can be used to
+ * indicate that the buffers contain depth measurement data instead of color
+ * images. The default dataSpace is 0, HAL_DATASPACE_UNKNOWN, unless it has been
+ * overridden by the consumer.
+ */
+static inline int native_window_set_buffers_data_space(
+ struct ANativeWindow* window,
+ android_dataspace_t dataSpace)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DATASPACE,
+ dataSpace);
+}
+
+/*
* native_window_set_buffers_transform(..., int transform)
* All buffers queued after this call will be displayed transformed according
* to the transform parameter specified.
@@ -906,6 +925,30 @@
sidebandHandle);
}
+/*
+ * native_window_set_surface_damage(..., android_native_rect_t* rects, int numRects)
+ * Set the surface damage (i.e., the region of the surface that has changed
+ * since the previous frame). The damage set by this call will be reset (to the
+ * default of full-surface damage) after calling queue, so this must be called
+ * prior to every frame with damage that does not cover the whole surface if the
+ * caller desires downstream consumers to use this optimization.
+ *
+ * The damage region is specified as an array of rectangles, with the important
+ * caveat that the origin of the surface is considered to be the bottom-left
+ * corner, as in OpenGL ES.
+ *
+ * If numRects is set to 0, rects may be NULL, and the surface damage will be
+ * set to the full surface (the same as if this function had not been called for
+ * this frame).
+ */
+static inline int native_window_set_surface_damage(
+ struct ANativeWindow* window,
+ const android_native_rect_t* rects, size_t numRects)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_SURFACE_DAMAGE,
+ rects, numRects);
+}
+
__END_DECLS
#endif /* SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H */
diff --git a/include/utils/Compat.h b/include/utils/Compat.h
index a238afe..7d96310 100644
--- a/include/utils/Compat.h
+++ b/include/utils/Compat.h
@@ -36,17 +36,17 @@
#endif /* __APPLE__ */
#if defined(_WIN32)
-#define O_CLOEXEC 0
+#define O_CLOEXEC O_NOINHERIT
#define O_NOFOLLOW 0
#define DEFFILEMODE 0666
#endif /* _WIN32 */
-#if HAVE_PRINTF_ZD
-# define ZD "%zd"
-# define ZD_TYPE ssize_t
+#if defined(_WIN32)
+#define ZD "%ld"
+#define ZD_TYPE long
#else
-# define ZD "%ld"
-# define ZD_TYPE long
+#define ZD "%zd"
+#define ZD_TYPE ssize_t
#endif
/*
diff --git a/include/utils/Timers.h b/include/utils/Timers.h
index d015421..54ec474 100644
--- a/include/utils/Timers.h
+++ b/include/utils/Timers.h
@@ -24,6 +24,8 @@
#include <sys/types.h>
#include <sys/time.h>
+#include <utils/Compat.h>
+
// ------------------------------------------------------------------
// C API
@@ -33,46 +35,46 @@
typedef int64_t nsecs_t; // nano-seconds
-static inline nsecs_t seconds_to_nanoseconds(nsecs_t secs)
+static CONSTEXPR inline nsecs_t seconds_to_nanoseconds(nsecs_t secs)
{
return secs*1000000000;
}
-static inline nsecs_t milliseconds_to_nanoseconds(nsecs_t secs)
+static CONSTEXPR inline nsecs_t milliseconds_to_nanoseconds(nsecs_t secs)
{
return secs*1000000;
}
-static inline nsecs_t microseconds_to_nanoseconds(nsecs_t secs)
+static CONSTEXPR inline nsecs_t microseconds_to_nanoseconds(nsecs_t secs)
{
return secs*1000;
}
-static inline nsecs_t nanoseconds_to_seconds(nsecs_t secs)
+static CONSTEXPR inline nsecs_t nanoseconds_to_seconds(nsecs_t secs)
{
return secs/1000000000;
}
-static inline nsecs_t nanoseconds_to_milliseconds(nsecs_t secs)
+static CONSTEXPR inline nsecs_t nanoseconds_to_milliseconds(nsecs_t secs)
{
return secs/1000000;
}
-static inline nsecs_t nanoseconds_to_microseconds(nsecs_t secs)
+static CONSTEXPR inline nsecs_t nanoseconds_to_microseconds(nsecs_t secs)
{
return secs/1000;
}
-static inline nsecs_t s2ns(nsecs_t v) {return seconds_to_nanoseconds(v);}
-static inline nsecs_t ms2ns(nsecs_t v) {return milliseconds_to_nanoseconds(v);}
-static inline nsecs_t us2ns(nsecs_t v) {return microseconds_to_nanoseconds(v);}
-static inline nsecs_t ns2s(nsecs_t v) {return nanoseconds_to_seconds(v);}
-static inline nsecs_t ns2ms(nsecs_t v) {return nanoseconds_to_milliseconds(v);}
-static inline nsecs_t ns2us(nsecs_t v) {return nanoseconds_to_microseconds(v);}
+static CONSTEXPR inline nsecs_t s2ns(nsecs_t v) {return seconds_to_nanoseconds(v);}
+static CONSTEXPR inline nsecs_t ms2ns(nsecs_t v) {return milliseconds_to_nanoseconds(v);}
+static CONSTEXPR inline nsecs_t us2ns(nsecs_t v) {return microseconds_to_nanoseconds(v);}
+static CONSTEXPR inline nsecs_t ns2s(nsecs_t v) {return nanoseconds_to_seconds(v);}
+static CONSTEXPR inline nsecs_t ns2ms(nsecs_t v) {return nanoseconds_to_milliseconds(v);}
+static CONSTEXPR inline nsecs_t ns2us(nsecs_t v) {return nanoseconds_to_microseconds(v);}
-static inline nsecs_t seconds(nsecs_t v) { return s2ns(v); }
-static inline nsecs_t milliseconds(nsecs_t v) { return ms2ns(v); }
-static inline nsecs_t microseconds(nsecs_t v) { return us2ns(v); }
+static CONSTEXPR inline nsecs_t seconds(nsecs_t v) { return s2ns(v); }
+static CONSTEXPR inline nsecs_t milliseconds(nsecs_t v) { return ms2ns(v); }
+static CONSTEXPR inline nsecs_t microseconds(nsecs_t v) { return us2ns(v); }
enum {
SYSTEM_TIME_REALTIME = 0, // system-wide realtime clock
diff --git a/include/zipfile/zipfile.h b/include/zipfile/zipfile.h
deleted file mode 100644
index 0ae4ee4..0000000
--- a/include/zipfile/zipfile.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _ZIPFILE_ZIPFILE_H
-#define _ZIPFILE_ZIPFILE_H
-
-#include <stddef.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef void* zipfile_t;
-typedef void* zipentry_t;
-
-// Provide a buffer. Returns NULL on failure.
-zipfile_t init_zipfile(const void* data, size_t size);
-
-// Release the zipfile resources.
-void release_zipfile(zipfile_t file);
-
-// Get a named entry object. Returns NULL if it doesn't exist
-// or if we won't be able to decompress it. The zipentry_t is
-// freed by release_zipfile()
-zipentry_t lookup_zipentry(zipfile_t file, const char* entryName);
-
-// Return the size of the entry.
-size_t get_zipentry_size(zipentry_t entry);
-
-// return the filename of this entry, you own the memory returned
-char* get_zipentry_name(zipentry_t entry);
-
-// The buffer must be 1.001 times the buffer size returned
-// by get_zipentry_size. Returns nonzero on failure.
-int decompress_zipentry(zipentry_t entry, void* buf, int bufsize);
-
-// iterate through the entries in the zip file. pass a pointer to
-// a void* initialized to NULL to start. Returns NULL when done
-zipentry_t iterate_zipfile(zipfile_t file, void** cookie);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif // _ZIPFILE_ZIPFILE_H
diff --git a/init/Android.mk b/init/Android.mk
index 5b8094f..b14f9b5 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -5,9 +5,9 @@
# --
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
-init_options += -DALLOW_LOCAL_PROP_OVERRIDE=1 -DALLOW_DISABLE_SELINUX=1
+init_options += -DALLOW_LOCAL_PROP_OVERRIDE=1 -DALLOW_PERMISSIVE_SELINUX=1
else
-init_options += -DALLOW_LOCAL_PROP_OVERRIDE=0 -DALLOW_DISABLE_SELINUX=0
+init_options += -DALLOW_LOCAL_PROP_OVERRIDE=0 -DALLOW_PERMISSIVE_SELINUX=0
endif
init_options += -DLOG_UEVENTS=0
@@ -18,17 +18,21 @@
-Wno-unused-parameter \
-Werror \
+init_clang := true
+
# --
include $(CLEAR_VARS)
LOCAL_CPPFLAGS := $(init_cflags)
LOCAL_SRC_FILES:= \
init_parser.cpp \
+ log.cpp \
parser.cpp \
util.cpp \
LOCAL_STATIC_LIBRARIES := libbase
LOCAL_MODULE := libinit
+LOCAL_CLANG := $(init_clang)
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
@@ -46,6 +50,9 @@
watchdogd.cpp \
LOCAL_MODULE:= init
+LOCAL_C_INCLUDES += \
+ system/extras/ext4_utils \
+ system/core/mkbootimg
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
@@ -54,20 +61,25 @@
LOCAL_STATIC_LIBRARIES := \
libinit \
libfs_mgr \
+ libsquashfs_utils \
liblogwrap \
libcutils \
libbase \
+ libext4_utils_static \
+ libutils \
liblog \
libc \
libselinux \
libmincrypt \
- libext4_utils_static
+ libc++_static \
+ libdl
# Create symlinks
LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd
+LOCAL_CLANG := $(init_clang)
include $(BUILD_EXECUTABLE)
@@ -84,4 +96,5 @@
libbase \
LOCAL_STATIC_LIBRARIES := libinit
+LOCAL_CLANG := $(init_clang)
include $(BUILD_NATIVE_TEST)
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index 530eba8..95687cb 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -30,6 +30,7 @@
#include <time.h>
#include <unistd.h>
+#include <memory>
#include <string>
#include <base/file.h>
@@ -114,9 +115,9 @@
static void do_log_procs(FILE* log) {
do_log_uptime(log);
- DIR* dir = opendir("/proc");
+ std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir("/proc"), closedir);
struct dirent* entry;
- while ((entry = readdir(dir)) != NULL) {
+ while ((entry = readdir(dir.get())) != NULL) {
// Only match numeric values.
char* end;
int pid = strtol(entry->d_name, &end, 10);
@@ -146,7 +147,6 @@
}
}
}
- closedir(dir);
fputc('\n', log);
}
@@ -195,13 +195,8 @@
}
// Create kernel process accounting file.
- {
- int fd = open( LOG_ACCT, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC,0644);
- if (fd >= 0) {
- close(fd);
- acct( LOG_ACCT );
- }
- }
+ close(open(LOG_ACCT, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
+ acct(LOG_ACCT);
log_header();
return count;
@@ -210,11 +205,12 @@
int do_bootchart_init(int nargs, char** args) {
g_remaining_samples = bootchart_init();
if (g_remaining_samples < 0) {
- ERROR("bootcharting init failure: %s\n", strerror(errno));
+ ERROR("Bootcharting init failure: %s\n", strerror(errno));
} else if (g_remaining_samples > 0) {
- NOTICE("bootcharting started (will run for %d ms)\n", g_remaining_samples*BOOTCHART_POLLING_MS);
+ NOTICE("Bootcharting started (will run for %d s).\n",
+ (g_remaining_samples * BOOTCHART_POLLING_MS) / 1000);
} else {
- NOTICE("bootcharting ignored\n");
+ NOTICE("Not bootcharting.\n");
}
return 0;
}
diff --git a/init/builtins.cpp b/init/builtins.cpp
index fb1aa7c..9e5f9ff 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -14,31 +14,32 @@
* limitations under the License.
*/
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdio.h>
-#include <linux/kd.h>
#include <errno.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <linux/if.h>
-#include <arpa/inet.h>
+#include <fcntl.h>
+#include <net/if.h>
+#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
#include <sys/mount.h>
#include <sys/resource.h>
#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <sys/wait.h>
+#include <unistd.h>
#include <linux/loop.h>
-#include <cutils/partition_utils.h>
-#include <cutils/android_reboot.h>
-#include <fs_mgr.h>
+#include <ext4_crypt_init_extensions.h>
#include <selinux/selinux.h>
#include <selinux/label.h>
+#include <fs_mgr.h>
+#include <base/stringprintf.h>
+#include <cutils/partition_utils.h>
+#include <cutils/android_reboot.h>
+#include <private/android_filesystem_config.h>
+
#include "init.h"
#include "keywords.h"
#include "property_service.h"
@@ -47,8 +48,6 @@
#include "util.h"
#include "log.h"
-#include <private/android_filesystem_config.h>
-
#define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
int add_environment(const char *name, const char *value);
@@ -58,8 +57,14 @@
static int insmod(const char *filename, char *options)
{
+ char filename_val[PROP_VALUE_MAX];
+ if (expand_props(filename_val, filename, sizeof(filename_val)) == -1) {
+ ERROR("insmod: cannot expand '%s'\n", filename);
+ return -EINVAL;
+ }
+
std::string module;
- if (!read_file(filename, &module)) {
+ if (!read_file(filename_val, &module)) {
return -1;
}
@@ -155,68 +160,6 @@
return 0;
}
-// TODO: remove execonce when exec is available.
-int do_execonce(int nargs, char **args)
-{
- pid_t child;
- int child_status = 0;
- static int already_done;
-
- if (already_done) {
- return -1;
- }
- already_done = 1;
- if (!(child = fork())) {
- /*
- * Child process.
- */
- zap_stdio();
- char *exec_args[100];
- size_t num_process_args = nargs;
-
- memset(exec_args, 0, sizeof(exec_args));
- if (num_process_args > ARRAY_SIZE(exec_args) - 1) {
- ERROR("exec called with %zu args, limit is %zu", num_process_args,
- ARRAY_SIZE(exec_args) - 1);
- _exit(1);
- }
- for (size_t i = 1; i < num_process_args; i++)
- exec_args[i - 1] = args[i];
-
- if (execv(exec_args[0], exec_args) == -1) {
- ERROR("Failed to execv '%s' (%s)", exec_args[0], strerror(errno));
- _exit(1);
- }
- ERROR("Returned from execv()!");
- _exit(1);
- }
-
- /*
- * Parent process.
- */
- if (child == -1) {
- ERROR("Fork failed\n");
- return -1;
- }
-
- if (TEMP_FAILURE_RETRY(waitpid(child, &child_status, 0)) == -1) {
- ERROR("waitpid(): failed (%s)\n", strerror(errno));
- return -1;
- }
-
- if (WIFSIGNALED(child_status)) {
- INFO("Child exited due to signal %d\n", WTERMSIG(child_status));
- return -1;
- } else if (WIFEXITED(child_status)) {
- INFO("Child exited normally (exit code %d)\n", WEXITSTATUS(child_status));
- return WEXITSTATUS(child_status);
- }
-
- ERROR("Abnormal child process exit\n");
-
- return -1;
-}
-
int do_export(int nargs, char **args)
{
return add_environment(args[1], args[2]);
@@ -304,7 +247,7 @@
}
}
- return 0;
+ return e4crypt_set_directory_policy(args[1]);
}
static struct {
@@ -448,7 +391,6 @@
while (1) { pause(); } // never reached
}
-
/*
* This function might request a reboot, in which case it will
* not return.
@@ -477,7 +419,7 @@
int wp_ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
if (wp_ret < 0) {
/* Unexpected error code. We will continue anyway. */
- NOTICE("waitpid failed rc=%d, errno=%d\n", wp_ret, errno);
+ NOTICE("waitpid failed rc=%d: %s\n", wp_ret, strerror(errno));
}
if (WIFEXITED(status)) {
@@ -504,6 +446,7 @@
property_set("vold.decrypt", "trigger_encryption");
} else if (ret == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
property_set("ro.crypto.state", "encrypted");
+ property_set("ro.crypto.type", "block");
property_set("vold.decrypt", "trigger_default_encryption");
} else if (ret == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
property_set("ro.crypto.state", "unencrypted");
@@ -516,6 +459,23 @@
ERROR("fs_mgr_mount_all suggested recovery, so wiping data via recovery.\n");
ret = wipe_data_via_recovery();
/* If reboot worked, there is no return. */
+ } else if (ret == FS_MGR_MNTALL_DEV_DEFAULT_FILE_ENCRYPTED) {
+ if (e4crypt_install_keyring()) {
+ return -1;
+ }
+ property_set("ro.crypto.state", "encrypted");
+ property_set("ro.crypto.type", "file");
+
+ // Although encrypted, we have device key, so we do not need to
+ // do anything different from the nonencrypted case.
+ action_for_each_trigger("nonencrypted", action_add_queue_tail);
+ } else if (ret == FS_MGR_MNTALL_DEV_NON_DEFAULT_FILE_ENCRYPTED) {
+ if (e4crypt_install_keyring()) {
+ return -1;
+ }
+ property_set("ro.crypto.state", "encrypted");
+ property_set("ro.crypto.type", "file");
+ property_set("vold.decrypt", "trigger_restart_min_framework");
} else if (ret > 0) {
ERROR("fs_mgr_mount_all returned unexpected error %d\n", ret);
}
@@ -536,15 +496,6 @@
return ret;
}
-int do_setcon(int nargs, char **args) {
- if (is_selinux_enabled() <= 0)
- return 0;
- if (setcon(args[1]) < 0) {
- return -errno;
- }
- return 0;
-}
-
int do_setprop(int nargs, char **args)
{
const char *name = args[1];
@@ -674,17 +625,21 @@
}
int do_verity_load_state(int nargs, char **args) {
- if (nargs == 1) {
- int mode = -1;
- int rc = fs_mgr_load_verity_state(&mode);
-
- if (rc == 0 && mode == VERITY_MODE_LOGGING) {
- action_for_each_trigger("verity-logging", action_add_queue_tail);
- }
-
- return rc;
+ int mode = -1;
+ int rc = fs_mgr_load_verity_state(&mode);
+ if (rc == 0 && mode == VERITY_MODE_LOGGING) {
+ action_for_each_trigger("verity-logging", action_add_queue_tail);
}
- return -1;
+ return rc;
+}
+
+static void verity_update_property(fstab_rec *fstab, const char *mount_point, int mode, int status) {
+ property_set(android::base::StringPrintf("partition.%s.verified", mount_point).c_str(),
+ android::base::StringPrintf("%d", mode).c_str());
+}
+
+int do_verity_update_state(int nargs, char** args) {
+ return fs_mgr_update_verity_state(verity_update_property);
}
int do_write(int nargs, char **args)
@@ -865,3 +820,31 @@
} else
return -1;
}
+
+/*
+ * Callback to make a directory from the ext4 code
+ */
+static int do_installkeys_ensure_dir_exists(const char* dir)
+{
+ if (make_dir(dir, 0700) && errno != EEXIST) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int do_installkey(int nargs, char **args)
+{
+ if (nargs != 2) {
+ return -1;
+ }
+
+ char prop_value[PROP_VALUE_MAX] = {0};
+ property_get("ro.crypto.type", prop_value);
+ if (strcmp(prop_value, "file")) {
+ return 0;
+ }
+
+ return e4crypt_create_device_key(args[1],
+ do_installkeys_ensure_dir_exists);
+}
diff --git a/init/devices.cpp b/init/devices.cpp
index 3a9b753..2c7f5a9 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -49,9 +49,9 @@
#include "log.h"
#define SYSFS_PREFIX "/sys"
-#define FIRMWARE_DIR1 "/etc/firmware"
-#define FIRMWARE_DIR2 "/vendor/firmware"
-#define FIRMWARE_DIR3 "/firmware/image"
+static const char *firmware_dirs[] = { "/etc/firmware",
+ "/vendor/firmware",
+ "/firmware/image" };
extern struct selabel_handle *sehandle;
@@ -266,7 +266,6 @@
static void add_platform_device(const char *path)
{
int path_len = strlen(path);
- struct listnode *node;
struct platform_node *bus;
const char *name = path;
@@ -276,15 +275,6 @@
name += 9;
}
- list_for_each_reverse(node, &platform_names) {
- bus = node_to_item(node, struct platform_node, list);
- if ((bus->path_len < path_len) &&
- (path[bus->path_len] == '/') &&
- !strncmp(path, bus->path, bus->path_len))
- /* subdevice of an existing platform, ignore it */
- return;
- }
-
INFO("adding platform device %s (%s)\n", name, path);
bus = (platform_node*) calloc(1, sizeof(struct platform_node));
@@ -364,13 +354,6 @@
return 0;
}
-static inline suseconds_t get_usecs(void)
-{
- struct timeval tv;
- gettimeofday(&tv, 0);
- return tv.tv_sec * (suseconds_t) 1000000 + tv.tv_usec;
-}
-
static void parse_event(const char *msg, struct uevent *uevent)
{
uevent->action = "";
@@ -818,8 +801,9 @@
static void process_firmware_event(struct uevent *uevent)
{
- char *root, *loading, *data, *file1 = NULL, *file2 = NULL, *file3 = NULL;
+ char *root, *loading, *data;
int l, loading_fd, data_fd, fw_fd;
+ size_t i;
int booting = is_booting();
INFO("firmware: loading '%s' for '%s'\n",
@@ -837,62 +821,49 @@
if (l == -1)
goto loading_free_out;
- l = asprintf(&file1, FIRMWARE_DIR1"/%s", uevent->firmware);
- if (l == -1)
- goto data_free_out;
-
- l = asprintf(&file2, FIRMWARE_DIR2"/%s", uevent->firmware);
- if (l == -1)
- goto data_free_out;
-
- l = asprintf(&file3, FIRMWARE_DIR3"/%s", uevent->firmware);
- if (l == -1)
- goto data_free_out;
-
loading_fd = open(loading, O_WRONLY|O_CLOEXEC);
if(loading_fd < 0)
- goto file_free_out;
+ goto data_free_out;
data_fd = open(data, O_WRONLY|O_CLOEXEC);
if(data_fd < 0)
goto loading_close_out;
try_loading_again:
- fw_fd = open(file1, O_RDONLY|O_CLOEXEC);
- if(fw_fd < 0) {
- fw_fd = open(file2, O_RDONLY|O_CLOEXEC);
- if (fw_fd < 0) {
- fw_fd = open(file3, O_RDONLY|O_CLOEXEC);
- if (fw_fd < 0) {
- if (booting) {
- /* If we're not fully booted, we may be missing
- * filesystems needed for firmware, wait and retry.
- */
- usleep(100000);
- booting = is_booting();
- goto try_loading_again;
- }
- INFO("firmware: could not open '%s' %d\n", uevent->firmware, errno);
- write(loading_fd, "-1", 2);
- goto data_close_out;
- }
+ for (i = 0; i < ARRAY_SIZE(firmware_dirs); i++) {
+ char *file = NULL;
+ l = asprintf(&file, "%s/%s", firmware_dirs[i], uevent->firmware);
+ if (l == -1)
+ goto data_free_out;
+ fw_fd = open(file, O_RDONLY|O_CLOEXEC);
+ free(file);
+ if (fw_fd >= 0) {
+ if(!load_firmware(fw_fd, loading_fd, data_fd))
+ INFO("firmware: copy success { '%s', '%s' }\n", root, uevent->firmware);
+ else
+ INFO("firmware: copy failure { '%s', '%s' }\n", root, uevent->firmware);
+ break;
}
}
-
- if(!load_firmware(fw_fd, loading_fd, data_fd))
- INFO("firmware: copy success { '%s', '%s' }\n", root, uevent->firmware);
- else
- INFO("firmware: copy failure { '%s', '%s' }\n", root, uevent->firmware);
+ if (fw_fd < 0) {
+ if (booting) {
+ /* If we're not fully booted, we may be missing
+ * filesystems needed for firmware, wait and retry.
+ */
+ usleep(100000);
+ booting = is_booting();
+ goto try_loading_again;
+ }
+ INFO("firmware: could not open '%s': %s\n", uevent->firmware, strerror(errno));
+ write(loading_fd, "-1", 2);
+ goto data_close_out;
+ }
close(fw_fd);
data_close_out:
close(data_fd);
loading_close_out:
close(loading_fd);
-file_free_out:
- free(file1);
- free(file2);
- free(file3);
data_free_out:
free(data);
loading_free_out:
@@ -1002,12 +973,7 @@
}
}
-void device_init(void)
-{
- suseconds_t t0, t1;
- struct stat info;
- int fd;
-
+void device_init() {
sehandle = NULL;
if (is_selinux_enabled() > 0) {
sehandle = selinux_android_file_context_handle();
@@ -1016,26 +982,22 @@
/* is 256K enough? udev uses 16MB! */
device_fd = uevent_open_socket(256*1024, true);
- if(device_fd < 0)
+ if (device_fd == -1) {
return;
-
- fcntl(device_fd, F_SETFD, FD_CLOEXEC);
+ }
fcntl(device_fd, F_SETFL, O_NONBLOCK);
- if (stat(COLDBOOT_DONE, &info) < 0) {
- t0 = get_usecs();
- coldboot("/sys/class");
- coldboot("/sys/block");
- coldboot("/sys/devices");
- t1 = get_usecs();
- fd = open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000);
- close(fd);
- if (LOG_UEVENTS) {
- INFO("coldboot %ld uS\n", ((long) (t1 - t0)));
- }
- } else if (LOG_UEVENTS) {
- INFO("skipping coldboot, already done\n");
+ if (access(COLDBOOT_DONE, F_OK) == 0) {
+ NOTICE("Skipping coldboot, already done!\n");
+ return;
}
+
+ Timer t;
+ coldboot("/sys/class");
+ coldboot("/sys/block");
+ coldboot("/sys/devices");
+ close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
+ NOTICE("Coldboot took %.2fs.\n", t.duration());
}
int get_device_fd()
diff --git a/init/init.cpp b/init/init.cpp
index 3c6e8a4..4f46560 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -15,6 +15,7 @@
*/
#include <ctype.h>
+#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
@@ -24,8 +25,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/epoll.h>
#include <sys/mount.h>
-#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -40,6 +41,9 @@
#include <selinux/label.h>
#include <selinux/android.h>
+#include <base/file.h>
+#include <base/stringprintf.h>
+#include <base/strings.h>
#include <cutils/android_reboot.h>
#include <cutils/fs.h>
#include <cutils/iosched_policy.h>
@@ -47,6 +51,8 @@
#include <cutils/sockets.h>
#include <private/android_filesystem_config.h>
+#include <memory>
+
#include "devices.h"
#include "init.h"
#include "log.h"
@@ -64,10 +70,6 @@
static int property_triggers_enabled = 0;
-static char console[32];
-static char bootmode[32];
-static char hardware[32];
-static unsigned revision = 0;
static char qemu[32];
static struct action *cur_action = NULL;
@@ -81,8 +83,19 @@
bool waiting_for_exec = false;
+static int epoll_fd = -1;
+
+void register_epoll_handler(int fd, void (*fn)()) {
+ epoll_event ev;
+ ev.events = EPOLLIN;
+ ev.data.ptr = reinterpret_cast<void*>(fn);
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+ ERROR("epoll_ctl failed: %s\n", strerror(errno));
+ }
+}
+
void service::NotifyStateChange(const char* new_state) {
- if (!properties_inited()) {
+ if (!properties_initialized()) {
// If properties aren't available yet, we can't set them.
return;
}
@@ -193,16 +206,15 @@
return;
}
- struct stat s;
- if (stat(svc->args[0], &s) != 0) {
- ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);
+ struct stat sb;
+ if (stat(svc->args[0], &sb) == -1) {
+ ERROR("cannot find '%s' (%s), disabling '%s'\n", svc->args[0], strerror(errno), svc->name);
svc->flags |= SVC_DISABLED;
return;
}
if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {
- ERROR("service '%s' must be one-shot to use dynamic args, disabling\n",
- svc->args[0]);
+ ERROR("service '%s' must be one-shot to use dynamic args, disabling\n", svc->args[0]);
svc->flags |= SVC_DISABLED;
return;
}
@@ -245,7 +257,7 @@
}
}
- NOTICE("starting '%s'\n", svc->name);
+ NOTICE("Starting service '%s'...\n", svc->name);
pid_t pid = fork();
if (pid == 0) {
@@ -255,7 +267,7 @@
int fd, sz;
umask(077);
- if (properties_inited()) {
+ if (properties_initialized()) {
get_property_workspace(&fd, &sz);
snprintf(tmp, sizeof(tmp), "%d,%d", dup(fd), sz);
add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
@@ -396,7 +408,7 @@
}
if (svc->pid) {
- NOTICE("service '%s' is being killed\n", svc->name);
+ NOTICE("Service '%s' is being killed...\n", svc->name);
kill(-svc->pid, SIGKILL);
svc->NotifyStateChange("stopping");
} else {
@@ -558,17 +570,18 @@
}
}
-void execute_one_command(void)
-{
- int ret, i;
+void execute_one_command() {
+ Timer t;
+
char cmd_str[256] = "";
char name_str[256] = "";
if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
cur_action = action_remove_queue_head();
cur_command = NULL;
- if (!cur_action)
+ if (!cur_action) {
return;
+ }
build_triggers_string(name_str, sizeof(name_str), cur_action);
@@ -578,31 +591,39 @@
cur_command = get_next_command(cur_action, cur_command);
}
- if (!cur_command)
+ if (!cur_command) {
return;
+ }
- ret = cur_command->func(cur_command->nargs, cur_command->args);
+ int result = cur_command->func(cur_command->nargs, cur_command->args);
if (klog_get_level() >= KLOG_INFO_LEVEL) {
- for (i = 0; i < cur_command->nargs; i++) {
+ for (int i = 0; i < cur_command->nargs; i++) {
strlcat(cmd_str, cur_command->args[i], sizeof(cmd_str));
if (i < cur_command->nargs - 1) {
strlcat(cmd_str, " ", sizeof(cmd_str));
}
}
- INFO("command '%s' action=%s status=%d (%s:%d)\n",
- cmd_str, cur_action ? name_str : "", ret, cur_command->filename,
- cur_command->line);
+ char source[256];
+ if (cur_command->filename) {
+ snprintf(source, sizeof(source), " (%s:%d)", cur_command->filename, cur_command->line);
+ } else {
+ *source = '\0';
+ }
+ INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
+ cmd_str, cur_action ? name_str : "", source, result, t.duration());
}
}
-static int wait_for_coldboot_done_action(int nargs, char **args)
-{
- int ret;
- INFO("wait for %s\n", COLDBOOT_DONE);
- ret = wait_for_file(COLDBOOT_DONE, COMMAND_RETRY_TIMEOUT);
- if (ret)
+static int wait_for_coldboot_done_action(int nargs, char **args) {
+ Timer t;
+
+ NOTICE("Waiting for %s...\n", COLDBOOT_DONE);
+ if (wait_for_file(COLDBOOT_DONE, COMMAND_RETRY_TIMEOUT)) {
ERROR("Timed out waiting for %s\n", COLDBOOT_DONE);
- return ret;
+ }
+
+ NOTICE("Waiting for %s took %.2fs.\n", COLDBOOT_DONE, t.duration());
+ return 0;
}
/*
@@ -679,7 +700,6 @@
if (urandom_fd != -1) {
close(urandom_fd);
}
- memset(buf, 0, sizeof(buf));
return result;
}
@@ -691,13 +711,12 @@
static int console_init_action(int nargs, char **args)
{
- int fd;
-
- if (console[0]) {
+ char console[PROP_VALUE_MAX];
+ if (property_get("ro.boot.console", console) > 0) {
snprintf(console_name, sizeof(console_name), "/dev/%s", console);
}
- fd = open(console_name, O_RDWR | O_CLOEXEC);
+ int fd = open(console_name, O_RDWR | O_CLOEXEC);
if (fd >= 0)
have_console = 1;
close(fd);
@@ -727,133 +746,83 @@
return 0;
}
-static void import_kernel_nv(char *name, int for_emulator)
-{
- char *value = strchr(name, '=');
- int name_len = strlen(name);
-
- if (value == 0) return;
- *value++ = 0;
- if (name_len == 0) return;
+static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
+ if (key.empty()) return;
if (for_emulator) {
- /* in the emulator, export any kernel option with the
- * ro.kernel. prefix */
- char buff[PROP_NAME_MAX];
- int len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );
-
- if (len < (int)sizeof(buff))
- property_set( buff, value );
+ // In the emulator, export any kernel option with the "ro.kernel." prefix.
+ property_set(android::base::StringPrintf("ro.kernel.%s", key.c_str()).c_str(), value.c_str());
return;
}
- if (!strcmp(name,"qemu")) {
- strlcpy(qemu, value, sizeof(qemu));
- } else if (!strncmp(name, "androidboot.", 12) && name_len > 12) {
- const char *boot_prop_name = name + 12;
- char prop[PROP_NAME_MAX];
- int cnt;
-
- cnt = snprintf(prop, sizeof(prop), "ro.boot.%s", boot_prop_name);
- if (cnt < PROP_NAME_MAX)
- property_set(prop, value);
+ if (key == "qemu") {
+ strlcpy(qemu, value.c_str(), sizeof(qemu));
+ } else if (android::base::StartsWith(key, "androidboot.")) {
+ property_set(android::base::StringPrintf("ro.boot.%s", key.c_str() + 12).c_str(),
+ value.c_str());
}
}
-static void export_kernel_boot_props(void)
-{
- char tmp[PROP_VALUE_MAX];
- int ret;
- unsigned i;
+static void export_kernel_boot_props() {
struct {
const char *src_prop;
- const char *dest_prop;
- const char *def_val;
+ const char *dst_prop;
+ const char *default_value;
} prop_map[] = {
- { "ro.boot.serialno", "ro.serialno", "", },
- { "ro.boot.mode", "ro.bootmode", "unknown", },
- { "ro.boot.baseband", "ro.baseband", "unknown", },
+ { "ro.boot.serialno", "ro.serialno", "", },
+ { "ro.boot.mode", "ro.bootmode", "unknown", },
+ { "ro.boot.baseband", "ro.baseband", "unknown", },
{ "ro.boot.bootloader", "ro.bootloader", "unknown", },
+ { "ro.boot.hardware", "ro.hardware", "unknown", },
+ { "ro.boot.revision", "ro.revision", "0", },
};
-
- for (i = 0; i < ARRAY_SIZE(prop_map); i++) {
- ret = property_get(prop_map[i].src_prop, tmp);
- if (ret > 0)
- property_set(prop_map[i].dest_prop, tmp);
- else
- property_set(prop_map[i].dest_prop, prop_map[i].def_val);
+ for (size_t i = 0; i < ARRAY_SIZE(prop_map); i++) {
+ char value[PROP_VALUE_MAX];
+ int rc = property_get(prop_map[i].src_prop, value);
+ property_set(prop_map[i].dst_prop, (rc > 0) ? value : prop_map[i].default_value);
}
-
- ret = property_get("ro.boot.console", tmp);
- if (ret)
- strlcpy(console, tmp, sizeof(console));
-
- /* save a copy for init's usage during boot */
- property_get("ro.bootmode", tmp);
- strlcpy(bootmode, tmp, sizeof(bootmode));
-
- /* if this was given on kernel command line, override what we read
- * before (e.g. from /proc/cpuinfo), if anything */
- ret = property_get("ro.boot.hardware", tmp);
- if (ret)
- strlcpy(hardware, tmp, sizeof(hardware));
- property_set("ro.hardware", hardware);
-
- snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
- property_set("ro.revision", tmp);
-
- /* TODO: these are obsolete. We should delete them */
- if (!strcmp(bootmode,"factory"))
- property_set("ro.factorytest", "1");
- else if (!strcmp(bootmode,"factory2"))
- property_set("ro.factorytest", "2");
- else
- property_set("ro.factorytest", "0");
}
-static void process_kernel_cmdline(void)
-{
- /* don't expose the raw commandline to nonpriv processes */
+static void process_kernel_dt() {
+ static const char android_dir[] = "/proc/device-tree/firmware/android";
+
+ std::string file_name = android::base::StringPrintf("%s/compatible", android_dir);
+
+ std::string dt_file;
+ android::base::ReadFileToString(file_name, &dt_file);
+ if (!dt_file.compare("android,firmware")) {
+ ERROR("firmware/android is not compatible with 'android,firmware'\n");
+ return;
+ }
+
+ std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(android_dir), closedir);
+ if (!dir) return;
+
+ struct dirent *dp;
+ while ((dp = readdir(dir.get())) != NULL) {
+ if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible")) {
+ continue;
+ }
+
+ file_name = android::base::StringPrintf("%s/%s", android_dir, dp->d_name);
+
+ android::base::ReadFileToString(file_name, &dt_file);
+ std::replace(dt_file.begin(), dt_file.end(), ',', '.');
+
+ std::string property_name = android::base::StringPrintf("ro.boot.%s", dp->d_name);
+ property_set(property_name.c_str(), dt_file.c_str());
+ }
+}
+
+static void process_kernel_cmdline() {
+ // Don't expose the raw commandline to unprivileged processes.
chmod("/proc/cmdline", 0440);
- /* first pass does the common stuff, and finds if we are in qemu.
- * second pass is only necessary for qemu to export all kernel params
- * as props.
- */
- import_kernel_cmdline(0, import_kernel_nv);
- if (qemu[0])
- import_kernel_cmdline(1, import_kernel_nv);
-
- /* now propogate the info given on command line to internal variables
- * used by init as well as the current required properties
- */
- export_kernel_boot_props();
-}
-
-static int property_service_init_action(int nargs, char **args)
-{
- /* read any property files on system or data and
- * fire up the property service. This must happen
- * after the ro.foo properties are set above so
- * that /data/local.prop cannot interfere with them.
- */
- start_property_service();
- if (get_property_set_fd() < 0) {
- ERROR("start_property_service() failed\n");
- exit(1);
- }
-
- return 0;
-}
-
-static int signal_init_action(int nargs, char **args)
-{
- signal_init();
- if (get_signal_fd() < 0) {
- ERROR("signal_init() failed\n");
- exit(1);
- }
- return 0;
+ // The first pass does the common stuff, and finds if we are in qemu.
+ // The second pass is only necessary for qemu to export all kernel params
+ // as properties.
+ import_kernel_cmdline(false, import_kernel_nv);
+ if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
}
static int queue_property_triggers_action(int nargs, char **args)
@@ -864,59 +833,37 @@
return 0;
}
-void selinux_init_all_handles(void)
+static void selinux_init_all_handles(void)
{
sehandle = selinux_android_file_context_handle();
selinux_android_set_sehandle(sehandle);
sehandle_prop = selinux_android_prop_context_handle();
}
-static bool selinux_is_disabled(void)
-{
- if (ALLOW_DISABLE_SELINUX) {
- if (access("/sys/fs/selinux", F_OK) != 0) {
- // SELinux is not compiled into the kernel, or has been disabled
- // via the kernel command line "selinux=0".
- return true;
- }
+enum selinux_enforcing_status { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
- char tmp[PROP_VALUE_MAX];
- if ((property_get("ro.boot.selinux", tmp) != 0) && (strcmp(tmp, "disabled") == 0)) {
- // SELinux is compiled into the kernel, but we've been told to disable it.
- return true;
- }
- }
+static selinux_enforcing_status selinux_status_from_cmdline() {
+ selinux_enforcing_status status = SELINUX_ENFORCING;
- return false;
+ import_kernel_cmdline(false, [&](const std::string& key, const std::string& value, bool in_qemu) {
+ if (key == "androidboot.selinux" && value == "permissive") {
+ status = SELINUX_PERMISSIVE;
+ }
+ });
+
+ return status;
}
static bool selinux_is_enforcing(void)
{
- if (ALLOW_DISABLE_SELINUX) {
- char tmp[PROP_VALUE_MAX];
- if (property_get("ro.boot.selinux", tmp) == 0) {
- // Property is not set. Assume enforcing.
- return true;
- }
-
- if (strcmp(tmp, "permissive") == 0) {
- // SELinux is in the kernel, but we've been told to go into permissive mode.
- return false;
- }
-
- if (strcmp(tmp, "enforcing") != 0) {
- ERROR("SELinux: Unknown value of ro.boot.selinux. Got: \"%s\". Assuming enforcing.\n", tmp);
- }
+ if (ALLOW_PERMISSIVE_SELINUX) {
+ return selinux_status_from_cmdline() == SELINUX_ENFORCING;
}
return true;
}
int selinux_reload_policy(void)
{
- if (selinux_is_disabled()) {
- return -1;
- }
-
INFO("SELinux: Attempting to reload policy files\n");
if (selinux_android_reload_policy() == -1) {
@@ -933,79 +880,80 @@
return 0;
}
-static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_t len)
-{
+static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
snprintf(buf, len, "property=%s", !data ? "NULL" : (char *)data);
return 0;
}
-int log_callback(int type, const char *fmt, ...)
-{
- int level;
- va_list ap;
- switch (type) {
- case SELINUX_WARNING:
- level = KLOG_WARNING_LEVEL;
- break;
- case SELINUX_INFO:
- level = KLOG_INFO_LEVEL;
- break;
- default:
- level = KLOG_ERROR_LEVEL;
- break;
- }
- va_start(ap, fmt);
- klog_vwrite(level, fmt, ap);
- va_end(ap);
- return 0;
+static void security_failure() {
+ ERROR("Security failure; rebooting into recovery mode...\n");
+ android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
+ while (true) { pause(); } // never reached
}
-static void selinux_initialize(void)
-{
- if (selinux_is_disabled()) {
- return;
- }
+static void selinux_initialize(bool in_kernel_domain) {
+ Timer t;
- INFO("loading selinux policy\n");
- if (selinux_android_load_policy() < 0) {
- ERROR("SELinux: Failed to load policy; rebooting into recovery mode\n");
- android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
- while (1) { pause(); } // never reached
- }
+ selinux_callback cb;
+ cb.func_log = selinux_klog_callback;
+ selinux_set_callback(SELINUX_CB_LOG, cb);
+ cb.func_audit = audit_callback;
+ selinux_set_callback(SELINUX_CB_AUDIT, cb);
- selinux_init_all_handles();
- bool is_enforcing = selinux_is_enforcing();
- INFO("SELinux: security_setenforce(%d)\n", is_enforcing);
- security_setenforce(is_enforcing);
+ if (in_kernel_domain) {
+ INFO("Loading SELinux policy...\n");
+ if (selinux_android_load_policy() < 0) {
+ ERROR("failed to load policy: %s\n", strerror(errno));
+ security_failure();
+ }
+
+ bool kernel_enforcing = (security_getenforce() == 1);
+ bool is_enforcing = selinux_is_enforcing();
+ if (kernel_enforcing != is_enforcing) {
+ if (security_setenforce(is_enforcing)) {
+ ERROR("security_setenforce(%s) failed: %s\n",
+ is_enforcing ? "true" : "false", strerror(errno));
+ security_failure();
+ }
+ }
+
+ if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
+ security_failure();
+ }
+
+ NOTICE("(Initializing SELinux %s took %.2fs.)\n",
+ is_enforcing ? "enforcing" : "non-enforcing", t.duration());
+ } else {
+ selinux_init_all_handles();
+ }
}
int main(int argc, char** argv) {
- if (!strcmp(basename(argv[0]), "ueventd"))
+ if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
+ }
- if (!strcmp(basename(argv[0]), "watchdogd"))
+ if (!strcmp(basename(argv[0]), "watchdogd")) {
return watchdogd_main(argc, argv);
+ }
// Clear the umask.
umask(0);
add_environment("PATH", _PATH_DEFPATH);
+ bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
+
// Get the basic filesystem setup we need put together in the initramdisk
// on / and then we'll let the rc file figure out the rest.
- mkdir("/dev", 0755);
- mkdir("/proc", 0755);
- mkdir("/sys", 0755);
-
- mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
- mkdir("/dev/pts", 0755);
- mkdir("/dev/socket", 0755);
- mount("devpts", "/dev/pts", "devpts", 0, NULL);
- mount("proc", "/proc", "proc", 0, NULL);
- mount("sysfs", "/sys", "sysfs", 0, NULL);
-
- // Indicate that booting is in progress to background fw loaders, etc.
- close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
+ if (is_first_stage) {
+ mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
+ mkdir("/dev/pts", 0755);
+ mkdir("/dev/socket", 0755);
+ mount("devpts", "/dev/pts", "devpts", 0, NULL);
+ mount("proc", "/proc", "proc", 0, NULL);
+ mount("sysfs", "/sys", "sysfs", 0, NULL);
+ }
// We must have some place other than / to create the device nodes for
// kmsg and null, otherwise we won't be able to remount / read-only
@@ -1013,51 +961,85 @@
// to the outside world.
open_devnull_stdio();
klog_init();
- property_init();
+ klog_set_level(KLOG_NOTICE_LEVEL);
- get_hardware_name(hardware, &revision);
+ NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");
- process_kernel_cmdline();
+ if (!is_first_stage) {
+ // Indicate that booting is in progress to background fw loaders, etc.
+ close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
- selinux_callback cb;
- cb.func_log = log_callback;
- selinux_set_callback(SELINUX_CB_LOG, cb);
- cb.func_audit = audit_callback;
- selinux_set_callback(SELINUX_CB_AUDIT, cb);
+ property_init();
- selinux_initialize();
+ // If arguments are passed both on the command line and in DT,
+ // properties set in DT always have priority over the command-line ones.
+ process_kernel_dt();
+ process_kernel_cmdline();
+
+ // Propagate the kernel variables to internal variables
+ // used by init as well as the current required properties.
+ export_kernel_boot_props();
+ }
+
+ // Set up SELinux, including loading the SELinux policy if we're in the kernel domain.
+ selinux_initialize(is_first_stage);
+
+ // If we're in the kernel domain, re-exec init to transition to the init domain now
+ // that the SELinux policy has been loaded.
+ if (is_first_stage) {
+ if (restorecon("/init") == -1) {
+ ERROR("restorecon failed: %s\n", strerror(errno));
+ security_failure();
+ }
+ char* path = argv[0];
+ char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
+ if (execv(path, args) == -1) {
+ ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
+ security_failure();
+ }
+ }
// These directories were necessarily created before initial policy load
// and therefore need their security context restored to the proper value.
// This must happen before /dev is populated by ueventd.
+ NOTICE("Running restorecon...\n");
restorecon("/dev");
restorecon("/dev/socket");
restorecon("/dev/__properties__");
restorecon_recursive("/sys");
- INFO("property init\n");
+ epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+ if (epoll_fd == -1) {
+ ERROR("epoll_create1 failed: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ signal_handler_init();
+
property_load_boot_defaults();
+ start_property_service();
init_parse_config_file("/init.rc");
action_for_each_trigger("early-init", action_add_queue_tail);
+ // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
+ // ... so that we can start queuing up actions that require stuff from /dev.
queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
queue_builtin_action(keychord_init_action, "keychord_init");
queue_builtin_action(console_init_action, "console_init");
- // Execute all the boot actions to get us started.
+ // Trigger all the boot actions to get us started.
action_for_each_trigger("init", action_add_queue_tail);
// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
// wasn't ready immediately after wait_for_coldboot_done
queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
- queue_builtin_action(property_service_init_action, "property_service_init");
- queue_builtin_action(signal_init_action, "signal_init");
// Don't mount filesystems or start core system services in charger mode.
- if (strcmp(bootmode, "charger") == 0) {
+ char bootmode[PROP_VALUE_MAX];
+ if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) {
action_for_each_trigger("charger", action_add_queue_tail);
} else {
action_for_each_trigger("late-init", action_add_queue_tail);
@@ -1066,41 +1048,12 @@
// Run all property triggers based on current state of the properties.
queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
- // TODO: why do we only initialize ufds after execute_one_command and restart_processes?
- size_t fd_count = 0;
- struct pollfd ufds[3];
- bool property_set_fd_init = false;
- bool signal_fd_init = false;
- bool keychord_fd_init = false;
-
- for (;;) {
+ while (true) {
if (!waiting_for_exec) {
execute_one_command();
restart_processes();
}
- if (!property_set_fd_init && get_property_set_fd() > 0) {
- ufds[fd_count].fd = get_property_set_fd();
- ufds[fd_count].events = POLLIN;
- ufds[fd_count].revents = 0;
- fd_count++;
- property_set_fd_init = true;
- }
- if (!signal_fd_init && get_signal_fd() > 0) {
- ufds[fd_count].fd = get_signal_fd();
- ufds[fd_count].events = POLLIN;
- ufds[fd_count].revents = 0;
- fd_count++;
- signal_fd_init = true;
- }
- if (!keychord_fd_init && get_keychord_fd() > 0) {
- ufds[fd_count].fd = get_keychord_fd();
- ufds[fd_count].events = POLLIN;
- ufds[fd_count].revents = 0;
- fd_count++;
- keychord_fd_init = true;
- }
-
int timeout = -1;
if (process_needs_restart) {
timeout = (process_needs_restart - gettime()) * 1000;
@@ -1114,21 +1067,12 @@
bootchart_sample(&timeout);
- int nr = poll(ufds, fd_count, timeout);
- if (nr <= 0) {
- continue;
- }
-
- for (size_t i = 0; i < fd_count; i++) {
- if (ufds[i].revents & POLLIN) {
- if (ufds[i].fd == get_property_set_fd()) {
- handle_property_set_fd();
- } else if (ufds[i].fd == get_keychord_fd()) {
- handle_keychord();
- } else if (ufds[i].fd == get_signal_fd()) {
- handle_signal();
- }
- }
+ epoll_event ev;
+ int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
+ if (nr == -1) {
+ ERROR("epoll_wait failed: %s\n", strerror(errno));
+ } else if (nr == 1) {
+ ((void (*)()) ev.data.ptr)();
}
}
diff --git a/init/init.h b/init/init.h
index a104af6..1cabb14 100644
--- a/init/init.h
+++ b/init/init.h
@@ -155,4 +155,6 @@
void zap_stdio(void);
+void register_epoll_handler(int fd, void (*fn)());
+
#endif /* _INIT_INIT_H */
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index f3d34b2..df049ed 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -14,15 +14,16 @@
* limitations under the License.
*/
+#include <ctype.h>
#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdarg.h>
#include <string.h>
-#include <stddef.h>
-#include <ctype.h>
+#include <unistd.h>
#include "init.h"
#include "parser.h"
@@ -118,9 +119,9 @@
switch (*s++) {
case 'b':
if (!strcmp(s, "ootchart_init")) return K_bootchart_init;
+ break;
case 'c':
if (!strcmp(s, "opy")) return K_copy;
- if (!strcmp(s, "apability")) return K_capability;
if (!strcmp(s, "lass")) return K_class;
if (!strcmp(s, "lass_start")) return K_class_start;
if (!strcmp(s, "lass_stop")) return K_class_stop;
@@ -137,7 +138,6 @@
case 'e':
if (!strcmp(s, "nable")) return K_enable;
if (!strcmp(s, "xec")) return K_exec;
- if (!strcmp(s, "xeconce")) return K_execonce;
if (!strcmp(s, "xport")) return K_export;
break;
case 'g':
@@ -151,6 +151,7 @@
if (!strcmp(s, "fup")) return K_ifup;
if (!strcmp(s, "nsmod")) return K_insmod;
if (!strcmp(s, "mport")) return K_import;
+ if (!strcmp(s, "nstallkey")) return K_installkey;
break;
case 'k':
if (!strcmp(s, "eycodes")) return K_keycodes;
@@ -183,7 +184,6 @@
case 's':
if (!strcmp(s, "eclabel")) return K_seclabel;
if (!strcmp(s, "ervice")) return K_service;
- if (!strcmp(s, "etcon")) return K_setcon;
if (!strcmp(s, "etenv")) return K_setenv;
if (!strcmp(s, "etprop")) return K_setprop;
if (!strcmp(s, "etrlimit")) return K_setrlimit;
@@ -202,6 +202,7 @@
break;
case 'v':
if (!strcmp(s, "erity_load_state")) return K_verity_load_state;
+ if (!strcmp(s, "erity_update_state")) return K_verity_update_state;
break;
case 'w':
if (!strcmp(s, "rite")) return K_write;
@@ -349,7 +350,7 @@
struct import* import = (struct import*) calloc(1, sizeof(struct import));
import->filename = strdup(conf_file);
list_add_tail(import_list, &import->list);
- INFO("found import '%s', adding to import list", import->filename);
+ INFO("Added '%s' to import list\n", import->filename);
}
static void parse_new_section(struct parse_state *state, int kw,
@@ -381,13 +382,13 @@
static void parse_config(const char *fn, const std::string& data)
{
- struct parse_state state;
struct listnode import_list;
struct listnode *node;
char *args[INIT_PARSER_MAXARGS];
- int nargs;
- nargs = 0;
+ int nargs = 0;
+
+ parse_state state;
state.filename = fn;
state.line = 0;
state.ptr = strdup(data.c_str()); // TODO: fix this code!
@@ -425,26 +426,28 @@
parser_done:
list_for_each(node, &import_list) {
- struct import *import = node_to_item(node, struct import, list);
- int ret;
-
- ret = init_parse_config_file(import->filename);
- if (ret)
- ERROR("could not import file '%s' from '%s'\n",
- import->filename, fn);
+ struct import* import = node_to_item(node, struct import, list);
+ if (!init_parse_config_file(import->filename)) {
+ ERROR("could not import file '%s' from '%s': %s\n",
+ import->filename, fn, strerror(errno));
+ }
}
}
-int init_parse_config_file(const char* path) {
+bool init_parse_config_file(const char* path) {
INFO("Parsing %s...\n", path);
+ Timer t;
std::string data;
if (!read_file(path, &data)) {
- return -1;
+ return false;
}
+ data.push_back('\n'); // TODO: fix parse_config.
parse_config(path, data);
dump_parser_state();
- return 0;
+
+ NOTICE("(Parsing %s took %.2fs.)\n", path, t.duration());
+ return true;
}
static int valid_name(const char *name)
@@ -769,8 +772,6 @@
kw = lookup_keyword(args[0]);
switch (kw) {
- case K_capability:
- break;
case K_class:
if (nargs != 2) {
parse_error(state, "class option requires a classname\n");
@@ -943,7 +944,14 @@
for (i = 1; i < nargs; i++) {
if (!(i % 2)) {
if (strcmp(args[i], "&&")) {
+ struct listnode *node;
+ struct listnode *node2;
parse_error(state, "& is the only symbol allowed to concatenate actions\n");
+ list_for_each_safe(node, node2, &act->triggers) {
+ struct trigger *trigger = node_to_item(node, struct trigger, nlist);
+ free(trigger);
+ }
+ free(act);
return 0;
} else
continue;
diff --git a/init/init_parser.h b/init/init_parser.h
index 6348607..90f880f 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -31,7 +31,7 @@
void queue_all_property_triggers();
void queue_builtin_action(int (*func)(int nargs, char **args), const char *name);
-int init_parse_config_file(const char *fn);
+bool init_parse_config_file(const char* path);
int expand_props(char *dst, const char *src, int len);
service* make_exec_oneshot_service(int argc, char** argv);
diff --git a/init/keychords.cpp b/init/keychords.cpp
index d6464bd..10d9573 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -62,37 +62,7 @@
}
}
-void keychord_init()
-{
- int fd, ret;
-
- service_for_each(add_service_keycodes);
-
- /* nothing to do if no services require keychords */
- if (!keychords)
- return;
-
- fd = open("/dev/keychord", O_RDWR | O_CLOEXEC);
- if (fd < 0) {
- ERROR("could not open /dev/keychord\n");
- return;
- }
-
- ret = write(fd, keychords, keychords_length);
- if (ret != keychords_length) {
- ERROR("could not configure /dev/keychord %d (%d)\n", ret, errno);
- close(fd);
- fd = -1;
- }
-
- free(keychords);
- keychords = 0;
-
- keychord_fd = fd;
-}
-
-void handle_keychord()
-{
+static void handle_keychord() {
struct service *svc;
char adb_enabled[PROP_VALUE_MAX];
int ret;
@@ -109,7 +79,7 @@
if (!strcmp(adb_enabled, "running")) {
svc = service_find_by_keychord(id);
if (svc) {
- INFO("starting service %s from keychord\n", svc->name);
+ INFO("Starting service %s from keychord\n", svc->name);
service_start(svc, NULL);
} else {
ERROR("service for keychord %d not found\n", id);
@@ -117,7 +87,28 @@
}
}
-int get_keychord_fd()
-{
- return keychord_fd;
+void keychord_init() {
+ service_for_each(add_service_keycodes);
+
+ // Nothing to do if no services require keychords.
+ if (!keychords) {
+ return;
+ }
+
+ keychord_fd = TEMP_FAILURE_RETRY(open("/dev/keychord", O_RDWR | O_CLOEXEC));
+ if (keychord_fd == -1) {
+ ERROR("could not open /dev/keychord: %s\n", strerror(errno));
+ return;
+ }
+
+ int ret = write(keychord_fd, keychords, keychords_length);
+ if (ret != keychords_length) {
+ ERROR("could not configure /dev/keychord %d: %s\n", ret, strerror(errno));
+ close(keychord_fd);
+ }
+
+ free(keychords);
+ keychords = nullptr;
+
+ register_epoll_handler(keychord_fd, handle_keychord);
}
diff --git a/init/keychords.h b/init/keychords.h
index 070b858..d2723b7 100644
--- a/init/keychords.h
+++ b/init/keychords.h
@@ -19,9 +19,7 @@
struct service;
-void add_service_keycodes(struct service *svc);
-void keychord_init(void);
-void handle_keychord(void);
-int get_keychord_fd(void);
+void add_service_keycodes(service*);
+void keychord_init();
#endif
diff --git a/init/keywords.h b/init/keywords.h
index c8327c3..37f01b8 100644
--- a/init/keywords.h
+++ b/init/keywords.h
@@ -6,11 +6,11 @@
int do_domainname(int nargs, char **args);
int do_enable(int nargs, char **args);
int do_exec(int nargs, char **args);
-int do_execonce(int nargs, char **args);
int do_export(int nargs, char **args);
int do_hostname(int nargs, char **args);
int do_ifup(int nargs, char **args);
int do_insmod(int nargs, char **args);
+int do_installkey(int nargs, char **args);
int do_mkdir(int nargs, char **args);
int do_mount_all(int nargs, char **args);
int do_mount(int nargs, char **args);
@@ -20,7 +20,6 @@
int do_restorecon_recursive(int nargs, char **args);
int do_rm(int nargs, char **args);
int do_rmdir(int nargs, char **args);
-int do_setcon(int nargs, char **args);
int do_setprop(int nargs, char **args);
int do_setrlimit(int nargs, char **args);
int do_start(int nargs, char **args);
@@ -37,13 +36,13 @@
int do_load_persist_props(int nargs, char **args);
int do_load_all_props(int nargs, char **args);
int do_verity_load_state(int nargs, char **args);
+int do_verity_update_state(int nargs, char **args);
int do_wait(int nargs, char **args);
#define __MAKE_KEYWORD_ENUM__
#define KEYWORD(symbol, flags, nargs, func) K_##symbol,
enum {
K_UNKNOWN,
#endif
- KEYWORD(capability, OPTION, 0, 0)
KEYWORD(class, OPTION, 0, 0)
KEYWORD(class_start, COMMAND, 1, do_class_start)
KEYWORD(class_stop, COMMAND, 1, do_class_stop)
@@ -54,12 +53,12 @@
KEYWORD(domainname, COMMAND, 1, do_domainname)
KEYWORD(enable, COMMAND, 1, do_enable)
KEYWORD(exec, COMMAND, 1, do_exec)
- KEYWORD(execonce, COMMAND, 1, do_execonce)
KEYWORD(export, COMMAND, 2, do_export)
KEYWORD(group, OPTION, 0, 0)
KEYWORD(hostname, COMMAND, 1, do_hostname)
KEYWORD(ifup, COMMAND, 1, do_ifup)
KEYWORD(insmod, COMMAND, 1, do_insmod)
+ KEYWORD(installkey, COMMAND, 1, do_installkey)
KEYWORD(import, SECTION, 1, 0)
KEYWORD(keycodes, OPTION, 0, 0)
KEYWORD(mkdir, COMMAND, 1, do_mkdir)
@@ -76,7 +75,6 @@
KEYWORD(rmdir, COMMAND, 1, do_rmdir)
KEYWORD(seclabel, OPTION, 0, 0)
KEYWORD(service, SECTION, 0, 0)
- KEYWORD(setcon, COMMAND, 1, do_setcon)
KEYWORD(setenv, OPTION, 2, 0)
KEYWORD(setprop, COMMAND, 2, do_setprop)
KEYWORD(setrlimit, COMMAND, 3, do_setrlimit)
@@ -89,6 +87,7 @@
KEYWORD(sysclktz, COMMAND, 1, do_sysclktz)
KEYWORD(user, OPTION, 0, 0)
KEYWORD(verity_load_state, COMMAND, 0, do_verity_load_state)
+ KEYWORD(verity_update_state, COMMAND, 0, do_verity_update_state)
KEYWORD(wait, COMMAND, 1, do_wait)
KEYWORD(write, COMMAND, 2, do_write)
KEYWORD(copy, COMMAND, 2, do_copy)
diff --git a/init/log.cpp b/init/log.cpp
new file mode 100644
index 0000000..eb5ec42
--- /dev/null
+++ b/init/log.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "log.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+
+#include <selinux/selinux.h>
+
+#include <base/stringprintf.h>
+
+static void init_klog_vwrite(int level, const char* fmt, va_list ap) {
+ static const char* tag = basename(getprogname());
+
+ // The kernel's printk buffer is only 1024 bytes.
+ // TODO: should we automatically break up long lines into multiple lines?
+ // Or we could log but with something like "..." at the end?
+ char buf[1024];
+ size_t prefix_size = snprintf(buf, sizeof(buf), "<%d>%s: ", level, tag);
+ size_t msg_size = vsnprintf(buf + prefix_size, sizeof(buf) - prefix_size, fmt, ap);
+ if (msg_size >= sizeof(buf) - prefix_size) {
+ msg_size = snprintf(buf + prefix_size, sizeof(buf) - prefix_size,
+ "(%zu-byte message too long for printk)\n", msg_size);
+ }
+
+ iovec iov[1];
+ iov[0].iov_base = buf;
+ iov[0].iov_len = prefix_size + msg_size;
+
+ klog_writev(level, iov, 1);
+}
+
+void init_klog_write(int level, const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ init_klog_vwrite(level, fmt, ap);
+ va_end(ap);
+}
+
+int selinux_klog_callback(int type, const char *fmt, ...) {
+ int level = KLOG_ERROR_LEVEL;
+ if (type == SELINUX_WARNING) {
+ level = KLOG_WARNING_LEVEL;
+ } else if (type == SELINUX_INFO) {
+ level = KLOG_INFO_LEVEL;
+ }
+ va_list ap;
+ va_start(ap, fmt);
+ init_klog_vwrite(level, fmt, ap);
+ va_end(ap);
+ return 0;
+}
diff --git a/init/log.h b/init/log.h
index e9cb65a..b804d1f 100644
--- a/init/log.h
+++ b/init/log.h
@@ -19,10 +19,11 @@
#include <cutils/klog.h>
-#define ERROR(x...) KLOG_ERROR("init", x)
-#define NOTICE(x...) KLOG_NOTICE("init", x)
-#define INFO(x...) KLOG_INFO("init", x)
+#define ERROR(x...) init_klog_write(KLOG_ERROR_LEVEL, x)
+#define NOTICE(x...) init_klog_write(KLOG_NOTICE_LEVEL, x)
+#define INFO(x...) init_klog_write(KLOG_INFO_LEVEL, x)
-extern int log_callback(int type, const char *fmt, ...);
+void init_klog_write(int level, const char* fmt, ...) __printflike(2, 3);
+int selinux_klog_callback(int level, const char* fmt, ...) __printflike(2, 3);
#endif
diff --git a/init/property_service.cpp b/init/property_service.cpp
index ddb8050..0ee0351 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -26,6 +26,8 @@
#include <errno.h>
#include <sys/poll.h>
+#include <memory>
+
#include <cutils/misc.h>
#include <cutils/sockets.h>
#include <cutils/multiuser.h>
@@ -44,15 +46,21 @@
#include <selinux/selinux.h>
#include <selinux/label.h>
+#include <fs_mgr.h>
+#include <base/file.h>
+#include "bootimg.h"
+
#include "property_service.h"
#include "init.h"
#include "util.h"
#include "log.h"
#define PERSISTENT_PROPERTY_DIR "/data/property"
+#define FSTAB_PREFIX "/fstab."
+#define RECOVERY_MOUNT_POINT "/recovery"
static int persistent_properties_loaded = 0;
-static int property_area_inited = 0;
+static bool property_area_initialized = false;
static int property_set_fd = -1;
@@ -61,34 +69,25 @@
int fd;
};
-static int init_workspace(workspace *w, size_t size)
-{
- int fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW);
- if (fd < 0)
- return -1;
-
- w->size = size;
- w->fd = fd;
- return 0;
-}
-
static workspace pa_workspace;
-static int init_property_area(void)
-{
- if (property_area_inited)
- return -1;
+void property_init() {
+ if (property_area_initialized) {
+ return;
+ }
- if(__system_property_area_init())
- return -1;
+ property_area_initialized = true;
- if(init_workspace(&pa_workspace, 0))
- return -1;
+ if (__system_property_area_init()) {
+ return;
+ }
- fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);
-
- property_area_inited = 1;
- return 0;
+ pa_workspace.size = 0;
+ pa_workspace.fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
+ if (pa_workspace.fd == -1) {
+ ERROR("Failed to open %s: %s\n", PROP_FILENAME, strerror(errno));
+ return;
+ }
}
static int check_mac_perms(const char *name, char *sctx)
@@ -159,7 +158,7 @@
snprintf(tempPath, sizeof(tempPath), "%s/.temp.XXXXXX", PERSISTENT_PROPERTY_DIR);
fd = mkstemp(tempPath);
if (fd < 0) {
- ERROR("Unable to write persistent property to temp file %s errno: %d\n", tempPath, errno);
+ ERROR("Unable to write persistent property to temp file %s: %s\n", tempPath, strerror(errno));
return;
}
write(fd, value, strlen(value));
@@ -199,18 +198,14 @@
return true;
}
-int property_set(const char *name, const char *value)
-{
- prop_info *pi;
- int ret;
-
+static int property_set_impl(const char* name, const char* value) {
size_t namelen = strlen(name);
size_t valuelen = strlen(value);
if (!is_legal_property_name(name, namelen)) return -1;
if (valuelen >= PROP_VALUE_MAX) return -1;
- pi = (prop_info*) __system_property_find(name);
+ prop_info* pi = (prop_info*) __system_property_find(name);
if(pi != 0) {
/* ro.* properties may NEVER be modified once set */
@@ -218,10 +213,9 @@
__system_property_update(pi, value, valuelen);
} else {
- ret = __system_property_add(name, namelen, value, valuelen);
- if (ret < 0) {
- ERROR("Failed to set '%s'='%s'\n", name, value);
- return ret;
+ int rc = __system_property_add(name, namelen, value, valuelen);
+ if (rc < 0) {
+ return rc;
}
}
/* If name starts with "net." treat as a DNS property. */
@@ -250,7 +244,15 @@
return 0;
}
-void handle_property_set_fd()
+int property_set(const char* name, const char* value) {
+ int rc = property_set_impl(name, value);
+ if (rc == -1) {
+ ERROR("property_set(\"%s\", \"%s\") failed\n", name, value);
+ }
+ return rc;
+}
+
+static void handle_property_set_fd()
{
prop_msg msg;
int s;
@@ -284,15 +286,15 @@
close(s);
return;
} else if (nr < 0) {
- ERROR("sys_prop: error waiting for uid=%d to send property message. err=%d %s\n", cr.uid, errno, strerror(errno));
+ ERROR("sys_prop: error waiting for uid=%d to send property message: %s\n", cr.uid, strerror(errno));
close(s);
return;
}
r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT));
if(r != sizeof(prop_msg)) {
- ERROR("sys_prop: mis-match msg size received: %d expected: %zu errno: %d\n",
- r, sizeof(prop_msg), errno);
+ ERROR("sys_prop: mis-match msg size received: %d expected: %zu: %s\n",
+ r, sizeof(prop_msg), strerror(errno));
close(s);
return;
}
@@ -414,87 +416,80 @@
* Filter is used to decide which properties to load: NULL loads all keys,
* "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
*/
-static void load_properties_from_file(const char *fn, const char *filter)
-{
+static void load_properties_from_file(const char* filename, const char* filter) {
+ Timer t;
std::string data;
- if (read_file(fn, &data)) {
+ if (read_file(filename, &data)) {
+ data.push_back('\n');
load_properties(&data[0], filter);
}
+ NOTICE("(Loading properties from %s took %.2fs.)\n", filename, t.duration());
}
-static void load_persistent_properties()
-{
- DIR* dir = opendir(PERSISTENT_PROPERTY_DIR);
- int dir_fd;
- struct dirent* entry;
- char value[PROP_VALUE_MAX];
- int fd, length;
- struct stat sb;
+static void load_persistent_properties() {
+ persistent_properties_loaded = 1;
- if (dir) {
- dir_fd = dirfd(dir);
- while ((entry = readdir(dir)) != NULL) {
- if (strncmp("persist.", entry->d_name, strlen("persist.")))
- continue;
- if (entry->d_type != DT_REG)
- continue;
- /* open the file and read the property value */
- fd = openat(dir_fd, entry->d_name, O_RDONLY | O_NOFOLLOW);
- if (fd < 0) {
- ERROR("Unable to open persistent property file \"%s\" errno: %d\n",
- entry->d_name, errno);
- continue;
- }
- if (fstat(fd, &sb) < 0) {
- ERROR("fstat on property file \"%s\" failed errno: %d\n", entry->d_name, errno);
- close(fd);
- continue;
- }
-
- // File must not be accessible to others, be owned by root/root, and
- // not be a hard link to any other file.
- if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0)
- || (sb.st_uid != 0)
- || (sb.st_gid != 0)
- || (sb.st_nlink != 1)) {
- ERROR("skipping insecure property file %s (uid=%u gid=%u nlink=%u mode=%o)\n",
- entry->d_name, (unsigned int)sb.st_uid, (unsigned int)sb.st_gid,
- (unsigned int)sb.st_nlink, sb.st_mode);
- close(fd);
- continue;
- }
-
- length = read(fd, value, sizeof(value) - 1);
- if (length >= 0) {
- value[length] = 0;
- property_set(entry->d_name, value);
- } else {
- ERROR("Unable to read persistent property file %s errno: %d\n",
- entry->d_name, errno);
- }
- close(fd);
- }
- closedir(dir);
- } else {
- ERROR("Unable to open persistent property directory %s errno: %d\n", PERSISTENT_PROPERTY_DIR, errno);
+ std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(PERSISTENT_PROPERTY_DIR), closedir);
+ if (!dir) {
+ ERROR("Unable to open persistent property directory \"%s\": %s\n",
+ PERSISTENT_PROPERTY_DIR, strerror(errno));
+ return;
}
- persistent_properties_loaded = 1;
+ struct dirent* entry;
+ while ((entry = readdir(dir.get())) != NULL) {
+ if (strncmp("persist.", entry->d_name, strlen("persist."))) {
+ continue;
+ }
+ if (entry->d_type != DT_REG) {
+ continue;
+ }
+
+ // Open the file and read the property value.
+ int fd = openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW);
+ if (fd == -1) {
+ ERROR("Unable to open persistent property file \"%s\": %s\n",
+ entry->d_name, strerror(errno));
+ continue;
+ }
+
+ struct stat sb;
+ if (fstat(fd, &sb) == -1) {
+ ERROR("fstat on property file \"%s\" failed: %s\n", entry->d_name, strerror(errno));
+ close(fd);
+ continue;
+ }
+
+ // File must not be accessible to others, be owned by root/root, and
+ // not be a hard link to any other file.
+ if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || (sb.st_uid != 0) || (sb.st_gid != 0) ||
+ (sb.st_nlink != 1)) {
+ ERROR("skipping insecure property file %s (uid=%u gid=%u nlink=%u mode=%o)\n",
+ entry->d_name, (unsigned int)sb.st_uid, (unsigned int)sb.st_gid,
+ (unsigned int)sb.st_nlink, sb.st_mode);
+ close(fd);
+ continue;
+ }
+
+ char value[PROP_VALUE_MAX];
+ int length = read(fd, value, sizeof(value) - 1);
+ if (length >= 0) {
+ value[length] = 0;
+ property_set(entry->d_name, value);
+ } else {
+ ERROR("Unable to read persistent property file %s: %s\n",
+ entry->d_name, strerror(errno));
+ }
+ close(fd);
+ }
}
-void property_init(void)
-{
- init_property_area();
-}
-
-void property_load_boot_defaults(void)
-{
+void property_load_boot_defaults() {
load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);
}
-int properties_inited(void)
-{
- return property_area_inited;
+bool properties_initialized() {
+ return property_area_initialized;
}
static void load_override_properties() {
@@ -507,47 +502,79 @@
}
}
-
/* When booting an encrypted system, /data is not mounted when the
* property service is started, so any properties stored there are
* not loaded. Vold triggers init to load these properties once it
* has mounted /data.
*/
-void load_persist_props(void)
-{
+void load_persist_props(void) {
load_override_properties();
/* Read persistent properties after all default values have been loaded. */
load_persistent_properties();
}
-void load_all_props(void)
-{
+void load_recovery_id_prop() {
+ char fstab_filename[PROP_VALUE_MAX + sizeof(FSTAB_PREFIX)];
+ char propbuf[PROP_VALUE_MAX];
+ int ret = property_get("ro.hardware", propbuf);
+ if (!ret) {
+ ERROR("ro.hardware not set - unable to load recovery id\n");
+ return;
+ }
+ snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX "%s", propbuf);
+
+ std::unique_ptr<fstab, void(*)(fstab*)> tab(fs_mgr_read_fstab(fstab_filename),
+ fs_mgr_free_fstab);
+ if (!tab) {
+ ERROR("unable to read fstab %s: %s\n", fstab_filename, strerror(errno));
+ return;
+ }
+
+ fstab_rec* rec = fs_mgr_get_entry_for_mount_point(tab.get(), RECOVERY_MOUNT_POINT);
+ if (rec == NULL) {
+ ERROR("/recovery not specified in fstab\n");
+ return;
+ }
+
+ int fd = open(rec->blk_device, O_RDONLY);
+ if (fd == -1) {
+ ERROR("error opening block device %s: %s\n", rec->blk_device, strerror(errno));
+ return;
+ }
+
+ boot_img_hdr hdr;
+ if (android::base::ReadFully(fd, &hdr, sizeof(hdr))) {
+ std::string hex = bytes_to_hex(reinterpret_cast<uint8_t*>(hdr.id), sizeof(hdr.id));
+ property_set("ro.recovery_id", hex.c_str());
+ } else {
+ ERROR("error reading /recovery: %s\n", strerror(errno));
+ }
+
+ close(fd);
+}
+
+void load_all_props() {
load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL);
- load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT, NULL);
load_properties_from_file(PROP_PATH_VENDOR_BUILD, NULL);
- load_properties_from_file(PROP_PATH_BOOTIMAGE_BUILD, NULL);
load_properties_from_file(PROP_PATH_FACTORY, "ro.*");
load_override_properties();
/* Read persistent properties after all default values have been loaded. */
load_persistent_properties();
+
+ load_recovery_id_prop();
}
-void start_property_service(void)
-{
- int fd;
+void start_property_service() {
+ property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ 0666, 0, 0, NULL);
+ if (property_set_fd == -1) {
+ ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
+ exit(1);
+ }
- fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0, NULL);
- if(fd < 0) return;
- fcntl(fd, F_SETFD, FD_CLOEXEC);
- fcntl(fd, F_SETFL, O_NONBLOCK);
+ listen(property_set_fd, 8);
- listen(fd, 8);
- property_set_fd = fd;
-}
-
-int get_property_set_fd()
-{
- return property_set_fd;
+ register_epoll_handler(property_set_fd, handle_property_set_fd);
}
diff --git a/init/property_service.h b/init/property_service.h
index 6e7fc00..a27053d 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -20,7 +20,6 @@
#include <stddef.h>
#include <sys/system_properties.h>
-extern void handle_property_set_fd(void);
extern void property_init(void);
extern void property_load_boot_defaults(void);
extern void load_persist_props(void);
@@ -29,8 +28,7 @@
void get_property_workspace(int *fd, int *sz);
extern int __property_get(const char *name, char *value);
extern int property_set(const char *name, const char *value);
-extern int properties_inited();
-int get_property_set_fd(void);
+extern bool properties_initialized();
#ifndef __clang__
extern void __property_get_size_error()
diff --git a/init/readme.txt b/init/readme.txt
index 7443330..6b9c42d 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -137,32 +137,9 @@
Commands
--------
-exec [ <seclabel> [ <user> [ <group> ]* ] ] -- <command> [ <argument> ]*
- Fork and execute command with the given arguments. The command starts
- after "--" so that an optional security context, user, and supplementary
- groups can be provided. No other commands will be run until this one
- finishes.
-
-execonce <path> [ <argument> ]*
- Fork and execute a program (<path>). This will block until
- the program completes execution. This command can be run at most
- once during init's lifetime. Subsequent invocations are ignored.
- It is best to avoid execonce as unlike the builtin commands, it runs
- the risk of getting init "stuck".
-
-export <name> <value>
- Set the environment variable <name> equal to <value> in the
- global environment (which will be inherited by all processes
- started after this command is executed)
-
-ifup <interface>
- Bring the network interface <interface> online.
-
-import <filename>
- Parse an init config file, extending the current configuration.
-
-hostname <name>
- Set the host name.
+bootchart_init
+ Start bootcharting if configured (see below).
+ This is included in the default init.rc.
chmod <octal-mode> <path>
Change file access permissions.
@@ -175,9 +152,18 @@
not already running.
class_stop <serviceclass>
- Stop all services of the specified class if they are
+ Stop and disable all services of the specified class if they are
currently running.
+class_reset <serviceclass>
+ Stop all services of the specified class if they are
+ currently running, without disabling them. They can be restarted
+ later using class_start.
+
+copy <src> <dst>
+ Copies a file. Similar to write, but useful for binary/large
+ amounts of data.
+
domainname <name>
Set the domain name.
@@ -190,9 +176,37 @@
on property:ro.boot.myfancyhardware=1
enable my_fancy_service_for_my_fancy_hardware
+exec [ <seclabel> [ <user> [ <group> ]* ] ] -- <command> [ <argument> ]*
+ Fork and execute command with the given arguments. The command starts
+ after "--" so that an optional security context, user, and supplementary
+ groups can be provided. No other commands will be run until this one
+ finishes.
+
+export <name> <value>
+ Set the environment variable <name> equal to <value> in the
+ global environment (which will be inherited by all processes
+ started after this command is executed)
+
+hostname <name>
+ Set the host name.
+
+ifup <interface>
+ Bring the network interface <interface> online.
+
+import <filename>
+ Parse an init config file, extending the current configuration.
+
insmod <path>
Install the module at <path>
+load_all_props
+ Loads properties from /system, /vendor, et cetera.
+ This is included in the default init.rc.
+
+load_persist_props
+ Loads persistent properties when /data has been decrypted.
+ This is included in the default init.rc.
+
loglevel <level>
Sets the kernel log level to level. Properties are expanded within <level>.
@@ -202,6 +216,9 @@
owned by the root user and root group. If provided, the mode, owner and group
will be updated if the directory exists already.
+mount_all <fstab>
+ Calls fs_mgr_mount_all on the given fs_mgr-format fstab.
+
mount <type> <device> <dir> [ <flag> ]* [<options>]
Attempt to mount the named device at the directory <dir>
<device> may be of the form mtd@name to specify a mtd block
@@ -210,6 +227,13 @@
<options> include "barrier=1", "noauto_da_alloc", "discard", ... as
a comma separated string, eg: barrier=1,noauto_da_alloc
+powerctl
+ Internal implementation detail used to respond to changes to the
+ "sys.powerctl" system property, used to implement rebooting.
+
+restart <service>
+ Like stop, but doesn't disable the service.
+
restorecon <path> [ <path> ]*
Restore the file named by <path> to the security context specified
in the file_contexts configuration.
@@ -220,10 +244,13 @@
Recursively restore the directory tree named by <path> to the
security contexts specified in the file_contexts configuration.
-setcon <seclabel>
- Set the current process security context to the specified string.
- This is typically only used from early-init to set the init context
- before any other process is started.
+rm <path>
+ Calls unlink(2) on the given path. You might want to
+ use "exec -- rm ..." instead (provided the system partition is
+ already mounted).
+
+rmdir <path>
+ Calls rmdir(2) on the given path.
setprop <name> <value>
Set system property <name> to <value>. Properties are expanded
@@ -238,6 +265,9 @@
stop <service>
Stop a service from running if it is currently running.
+swapon_all <fstab>
+ Calls fs_mgr_swapon_all on the given fstab file.
+
symlink <target> <path>
Create a symbolic link at <path> with the value <target>
@@ -248,10 +278,18 @@
Trigger an event. Used to queue an action from another
action.
+verity_load_state
+ Internal implementation detail used to load dm-verity state.
+
+verity_update_state <mount_point>
+ Internal implementation detail used to update dm-verity state and
+ set the partition.<mount_point>.verified properties used by adb remount
+ because fs_mgr can't set them directly itself.
+
wait <path> [ <timeout> ]
- Poll for the existence of the given file and return when found,
- or the timeout has been reached. If timeout is not specified it
- currently defaults to five seconds.
+ Poll for the existence of the given file and return when found,
+ or the timeout has been reached. If timeout is not specified it
+ currently defaults to five seconds.
write <path> <content>
Open the file at <path> and write a string to it with write(2).
@@ -276,12 +314,11 @@
Bootcharting
------------
-
This version of init contains code to perform "bootcharting": generating log
files that can be later processed by the tools provided by www.bootchart.org.
-On the emulator, use the new -bootchart <timeout> option to boot with
-bootcharting activated for <timeout> seconds.
+On the emulator, use the -bootchart <timeout> option to boot with bootcharting
+activated for <timeout> seconds.
On a device, create /data/bootchart/start with a command like the following:
@@ -302,9 +339,13 @@
bootchart command-line utility:
sudo apt-get install pybootchartgui
- ANDROID_SERIAL=<device serial number>
+ # grab-bootchart.sh uses $ANDROID_SERIAL.
$ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
+One thing to watch for is that the bootchart will show init as if it started
+running at 0s. You'll have to look at dmesg to work out when the kernel
+actually started init.
+
Debugging init
--------------
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
index c428b96..39a466d 100644
--- a/init/signal_handler.cpp
+++ b/init/signal_handler.cpp
@@ -18,56 +18,67 @@
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
-#include <unistd.h>
-#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/types.h>
#include <sys/wait.h>
-#include <cutils/sockets.h>
+#include <unistd.h>
+
+#include <base/stringprintf.h>
#include <cutils/android_reboot.h>
#include <cutils/list.h>
+#include <cutils/sockets.h>
#include "init.h"
#include "log.h"
#include "util.h"
-static int signal_fd = -1;
-static int signal_recv_fd = -1;
-
-static void sigchld_handler(int s) {
- write(signal_fd, &s, 1);
-}
-
#define CRITICAL_CRASH_THRESHOLD 4 /* if we crash >4 times ... */
#define CRITICAL_CRASH_WINDOW (4*60) /* ... in 4 minutes, goto recovery */
-static int wait_for_one_process() {
+static int signal_write_fd = -1;
+static int signal_read_fd = -1;
+
+static std::string DescribeStatus(int status) {
+ if (WIFEXITED(status)) {
+ return android::base::StringPrintf("exited with status %d", WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ return android::base::StringPrintf("killed by signal %d", WTERMSIG(status));
+ } else if (WIFSTOPPED(status)) {
+ return android::base::StringPrintf("stopped by signal %d", WSTOPSIG(status));
+ } else {
+ return "state changed";
+ }
+}
+
+static bool wait_for_one_process() {
int status;
pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
- if (pid <= 0) {
- return -1;
+ if (pid == 0) {
+ return false;
+ } else if (pid == -1) {
+ ERROR("waitpid failed: %s\n", strerror(errno));
+ return false;
}
- INFO("waitpid returned pid %d, status = %08x\n", pid, status);
service* svc = service_find_by_pid(pid);
+
+ std::string name;
+ if (svc) {
+ name = android::base::StringPrintf("Service '%s' (pid %d)", svc->name, pid);
+ } else {
+ name = android::base::StringPrintf("Untracked pid %d", pid);
+ }
+
+ NOTICE("%s %s\n", name.c_str(), DescribeStatus(status).c_str());
+
if (!svc) {
- if (WIFEXITED(status)) {
- ERROR("untracked pid %d exited with status %d\n", pid, WEXITSTATUS(status));
- } else if (WIFSIGNALED(status)) {
- ERROR("untracked pid %d killed by signal %d\n", pid, WTERMSIG(status));
- } else if (WIFSTOPPED(status)) {
- ERROR("untracked pid %d stopped by signal %d\n", pid, WSTOPSIG(status));
- } else {
- ERROR("untracked pid %d state changed\n", pid);
- }
- return 0;
+ return true;
}
// TODO: all the code from here down should be a member function on service.
- NOTICE("process '%s', pid %d exited\n", svc->name, pid);
-
if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {
- NOTICE("process '%s' killing any children in process group\n", svc->name);
+ NOTICE("Service '%s' (pid %d) killing any children in process group\n", svc->name, pid);
kill(-pid, SIGKILL);
}
@@ -84,7 +95,7 @@
list_remove(&svc->slist);
free(svc->name);
free(svc);
- return 0;
+ return true;
}
svc->pid = 0;
@@ -99,7 +110,7 @@
// Disabled and reset processes do not get restarted automatically.
if (svc->flags & (SVC_DISABLED | SVC_RESET)) {
svc->NotifyStateChange("stopped");
- return 0;
+ return true;
}
time_t now = gettime();
@@ -110,7 +121,7 @@
"rebooting into recovery mode\n", svc->name,
CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
- return 0;
+ return true;
}
} else {
svc->time_crashed = now;
@@ -128,34 +139,47 @@
cmd->func(cmd->nargs, cmd->args);
}
svc->NotifyStateChange("restarting");
- return 0;
+ return true;
}
-void handle_signal() {
- // We got a SIGCHLD - reap and restart as needed.
- char tmp[32];
- read(signal_recv_fd, tmp, sizeof(tmp));
- while (!wait_for_one_process()) {
+static void reap_any_outstanding_children() {
+ while (wait_for_one_process()) {
}
}
-void signal_init() {
+static void handle_signal() {
+ // Clear outstanding requests.
+ char buf[32];
+ read(signal_read_fd, buf, sizeof(buf));
+
+ reap_any_outstanding_children();
+}
+
+static void SIGCHLD_handler(int) {
+ if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
+ ERROR("write(signal_write_fd) failed: %s\n", strerror(errno));
+ }
+}
+
+void signal_handler_init() {
+ // Create a signalling mechanism for SIGCHLD.
+ int s[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
+ ERROR("socketpair failed: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ signal_write_fd = s[0];
+ signal_read_fd = s[1];
+
+ // Write to signal_write_fd if we catch SIGCHLD.
struct sigaction act;
memset(&act, 0, sizeof(act));
- act.sa_handler = sigchld_handler;
+ act.sa_handler = SIGCHLD_handler;
act.sa_flags = SA_NOCLDSTOP;
sigaction(SIGCHLD, &act, 0);
- // Create a signalling mechanism for the sigchld handler.
- int s[2];
- if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == 0) {
- signal_fd = s[0];
- signal_recv_fd = s[1];
- }
+ reap_any_outstanding_children();
- handle_signal();
-}
-
-int get_signal_fd() {
- return signal_recv_fd;
+ register_epoll_handler(signal_read_fd, handle_signal);
}
diff --git a/init/signal_handler.h b/init/signal_handler.h
index b092ccb..449b4af 100644
--- a/init/signal_handler.h
+++ b/init/signal_handler.h
@@ -17,8 +17,6 @@
#ifndef _INIT_SIGNAL_HANDLER_H_
#define _INIT_SIGNAL_HANDLER_H_
-void signal_init(void);
-void handle_signal(void);
-int get_signal_fd(void);
+void signal_handler_init(void);
#endif
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index d56b91a..c63fdaa 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -14,46 +14,27 @@
* limitations under the License.
*/
-#include <poll.h>
-#include <fcntl.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
#include <ctype.h>
+#include <fcntl.h>
+#include <poll.h>
#include <signal.h>
-#include <selinux/selinux.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <base/stringprintf.h>
#include <private/android_filesystem_config.h>
+#include <selinux/selinux.h>
#include "ueventd.h"
#include "log.h"
#include "util.h"
#include "devices.h"
#include "ueventd_parser.h"
-
-static char hardware[32];
-static unsigned revision = 0;
-
-static void import_kernel_nv(char *name, int in_qemu)
-{
- if (*name != '\0') {
- char *value = strchr(name, '=');
- if (value != NULL) {
- *value++ = 0;
- if (!strcmp(name,"androidboot.hardware"))
- {
- strlcpy(hardware, value, sizeof(hardware));
- }
- }
- }
-}
+#include "property_service.h"
int ueventd_main(int argc, char **argv)
{
- struct pollfd ufd;
- int nr;
- char tmp[32];
-
/*
* init sets the umask to 077 for forked processes. We need to
* create files with exact permissions, without modification by
@@ -70,43 +51,35 @@
open_devnull_stdio();
klog_init();
- if (LOG_UEVENTS) {
- /* Ensure we're at a logging level that will show the events */
- if (klog_get_level() < KLOG_INFO_LEVEL) {
- klog_set_level(KLOG_INFO_LEVEL);
- }
- }
+ klog_set_level(KLOG_NOTICE_LEVEL);
- union selinux_callback cb;
- cb.func_log = log_callback;
+ NOTICE("ueventd started!\n");
+
+ selinux_callback cb;
+ cb.func_log = selinux_klog_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
- INFO("starting ueventd\n");
-
- /* Respect hardware passed in through the kernel cmd line. Here we will look
- * for androidboot.hardware param in kernel cmdline, and save its value in
- * hardware[]. */
- import_kernel_cmdline(0, import_kernel_nv);
-
- get_hardware_name(hardware, &revision);
+ char hardware[PROP_VALUE_MAX];
+ property_get("ro.hardware", hardware);
ueventd_parse_config_file("/ueventd.rc");
-
- snprintf(tmp, sizeof(tmp), "/ueventd.%s.rc", hardware);
- ueventd_parse_config_file(tmp);
+ ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware).c_str());
device_init();
+ pollfd ufd;
ufd.events = POLLIN;
ufd.fd = get_device_fd();
- while(1) {
+ while (true) {
ufd.revents = 0;
- nr = poll(&ufd, 1, -1);
- if (nr <= 0)
+ int nr = poll(&ufd, 1, -1);
+ if (nr <= 0) {
continue;
- if (ufd.revents & POLLIN)
- handle_device_fd();
+ }
+ if (ufd.revents & POLLIN) {
+ handle_device_fd();
+ }
}
return 0;
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 7a4841f..497c606 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -193,10 +193,10 @@
static void parse_config(const char *fn, const std::string& data)
{
- struct parse_state state;
char *args[UEVENTD_PARSER_MAXARGS];
- int nargs;
- nargs = 0;
+
+ int nargs = 0;
+ parse_state state;
state.filename = fn;
state.line = 1;
state.ptr = strdup(data.c_str()); // TODO: fix this code!
@@ -231,6 +231,7 @@
return -1;
}
+ data.push_back('\n'); // TODO: fix parse_config.
parse_config(fn, data);
dump_parser_state();
return 0;
diff --git a/init/util.cpp b/init/util.cpp
index c805083..9c62f68 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -33,9 +33,11 @@
#include <sys/un.h>
#include <base/file.h>
+#include <base/strings.h>
/* for ANDROID_SOCKET_* */
#include <cutils/sockets.h>
+#include <base/stringprintf.h>
#include <private/android_filesystem_config.h>
@@ -170,18 +172,19 @@
bool okay = android::base::ReadFdToString(fd, content);
TEMP_FAILURE_RETRY(close(fd));
- if (okay) {
- content->append("\n", 1);
- }
return okay;
}
int write_file(const char* path, const char* content) {
int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY|O_CREAT|O_NOFOLLOW|O_CLOEXEC, 0600));
if (fd == -1) {
- return -errno;
+ NOTICE("write_file: Unable to open '%s': %s\n", path, strerror(errno));
+ return -1;
}
- int result = android::base::WriteStringToFd(content, fd) ? 0 : -errno;
+ int result = android::base::WriteStringToFd(content, fd) ? 0 : -1;
+ if (result == -1) {
+ NOTICE("write_file: Unable to write to '%s': %s\n", path, strerror(errno));
+ }
TEMP_FAILURE_RETRY(close(fd));
return result;
}
@@ -258,22 +261,16 @@
return -1;
}
-/*
- * gettime() - returns the time in seconds of the system's monotonic clock or
- * zero on error.
- */
-time_t gettime(void)
-{
- struct timespec ts;
- int ret;
+time_t gettime() {
+ timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ return now.tv_sec;
+}
- ret = clock_gettime(CLOCK_MONOTONIC, &ts);
- if (ret < 0) {
- ERROR("clock_gettime(CLOCK_MONOTONIC) failed: %s\n", strerror(errno));
- return 0;
- }
-
- return ts.tv_sec;
+uint64_t gettime_ns() {
+ timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000000) + now.tv_nsec;
}
int mkdir_recursive(const char *pathname, mode_t mode)
@@ -381,85 +378,40 @@
void open_devnull_stdio(void)
{
- int fd;
- static const char *name = "/dev/__null__";
- if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
- fd = open(name, O_RDWR);
- unlink(name);
- if (fd >= 0) {
- dup2(fd, 0);
- dup2(fd, 1);
- dup2(fd, 2);
- if (fd > 2) {
- close(fd);
- }
- return;
+ // Try to avoid the mknod() call if we can. Since SELinux makes
+ // a /dev/null replacement available for free, let's use it.
+ int fd = open("/sys/fs/selinux/null", O_RDWR);
+ if (fd == -1) {
+ // OOPS, /sys/fs/selinux/null isn't available, likely because
+ // /sys/fs/selinux isn't mounted. Fall back to mknod.
+ static const char *name = "/dev/__null__";
+ if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
+ fd = open(name, O_RDWR);
+ unlink(name);
+ }
+ if (fd == -1) {
+ exit(1);
}
}
- exit(1);
-}
-
-void get_hardware_name(char *hardware, unsigned int *revision) {
- // Hardware string was provided on kernel command line.
- if (hardware[0]) {
- return;
- }
-
- FILE* fp = fopen("/proc/cpuinfo", "re");
- if (fp == NULL) {
- return;
- }
- char buf[1024];
- while (fgets(buf, sizeof(buf), fp) != NULL) {
- if (strncmp(buf, "Hardware", 8) == 0) {
- const char* hw = strstr(buf, ": ");
- if (hw) {
- hw += 2;
- size_t n = 0;
- while (*hw) {
- if (!isspace(*hw)) {
- hardware[n++] = tolower(*hw);
- }
- hw++;
- if (n == 31) break;
- }
- hardware[n] = 0;
- }
- } else if (strncmp(buf, "Revision", 8) == 0) {
- sscanf(buf, "Revision : %ux", revision);
- }
- }
- fclose(fp);
-}
-
-void import_kernel_cmdline(int in_qemu,
- void (*import_kernel_nv)(char *name, int in_qemu))
-{
- char cmdline[2048];
- char *ptr;
- int fd;
-
- fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC);
- if (fd >= 0) {
- int n = read(fd, cmdline, sizeof(cmdline) - 1);
- if (n < 0) n = 0;
-
- /* get rid of trailing newline, it happens */
- if (n > 0 && cmdline[n-1] == '\n') n--;
-
- cmdline[n] = 0;
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ if (fd > 2) {
close(fd);
- } else {
- cmdline[0] = 0;
}
+}
- ptr = cmdline;
- while (ptr && *ptr) {
- char *x = strchr(ptr, ' ');
- if (x != 0) *x++ = 0;
- import_kernel_nv(ptr, in_qemu);
- ptr = x;
+void import_kernel_cmdline(bool in_qemu,
+ std::function<void(const std::string&, const std::string&, bool)> fn) {
+ std::string cmdline;
+ android::base::ReadFileToString("/proc/cmdline", &cmdline);
+
+ for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
+ std::vector<std::string> pieces = android::base::Split(entry, "=");
+ if (pieces.size() == 2) {
+ fn(pieces[0], pieces[1], in_qemu);
+ }
}
}
@@ -495,3 +447,13 @@
{
return selinux_android_restorecon(pathname, SELINUX_ANDROID_RESTORECON_RECURSE);
}
+
+/*
+ * Writes hex_len hex characters (1/2 byte) to hex from bytes.
+ */
+std::string bytes_to_hex(const uint8_t* bytes, size_t bytes_len) {
+ std::string hex("0x");
+ for (size_t i = 0; i < bytes_len; i++)
+ android::base::StringAppendF(&hex, "%02x", bytes[i]);
+ return hex;
+}
diff --git a/init/util.h b/init/util.h
index 77da3ac..3aba599 100644
--- a/init/util.h
+++ b/init/util.h
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include <string>
+#include <functional>
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
@@ -33,7 +34,22 @@
bool read_file(const char* path, std::string* content);
int write_file(const char* path, const char* content);
-time_t gettime(void);
+time_t gettime();
+uint64_t gettime_ns();
+
+class Timer {
+ public:
+ Timer() : t0(gettime_ns()) {
+ }
+
+ double duration() {
+ return static_cast<double>(gettime_ns() - t0) / 1000000000.0;
+ }
+
+ private:
+ uint64_t t0;
+};
+
unsigned int decode_uid(const char *s);
int mkdir_recursive(const char *pathname, mode_t mode);
@@ -42,9 +58,10 @@
void remove_link(const char *oldpath, const char *newpath);
int wait_for_file(const char *filename, int timeout);
void open_devnull_stdio(void);
-void get_hardware_name(char *hardware, unsigned int *revision);
-void import_kernel_cmdline(int in_qemu, void (*import_kernel_nv)(char *name, int in_qemu));
+void import_kernel_cmdline(bool in_qemu,
+ std::function<void(const std::string&, const std::string&, bool)>);
int make_dir(const char *path, mode_t mode);
int restorecon(const char *pathname);
int restorecon_recursive(const char *pathname);
+std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len);
#endif
diff --git a/init/watchdogd.cpp b/init/watchdogd.cpp
index 0790811..881a4df 100644
--- a/init/watchdogd.cpp
+++ b/init/watchdogd.cpp
@@ -27,52 +27,45 @@
#define DEV_NAME "/dev/watchdog"
-int watchdogd_main(int argc, char **argv)
-{
- int fd;
- int ret;
- int interval = 10;
- int margin = 10;
- int timeout;
-
+int watchdogd_main(int argc, char **argv) {
open_devnull_stdio();
klog_init();
+ klog_set_level(KLOG_NOTICE_LEVEL);
- INFO("Starting watchdogd\n");
+ int interval = 10;
+ if (argc >= 2) interval = atoi(argv[1]);
- if (argc >= 2)
- interval = atoi(argv[1]);
+ int margin = 10;
+ if (argc >= 3) margin = atoi(argv[2]);
- if (argc >= 3)
- margin = atoi(argv[2]);
+ NOTICE("watchdogd started (interval %d, margin %d)!\n", interval, margin);
- timeout = interval + margin;
-
- fd = open(DEV_NAME, O_RDWR|O_CLOEXEC);
- if (fd < 0) {
+ int fd = open(DEV_NAME, O_RDWR|O_CLOEXEC);
+ if (fd == -1) {
ERROR("watchdogd: Failed to open %s: %s\n", DEV_NAME, strerror(errno));
return 1;
}
- ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
+ int timeout = interval + margin;
+ int ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
if (ret) {
ERROR("watchdogd: Failed to set timeout to %d: %s\n", timeout, strerror(errno));
ret = ioctl(fd, WDIOC_GETTIMEOUT, &timeout);
if (ret) {
ERROR("watchdogd: Failed to get timeout: %s\n", strerror(errno));
} else {
- if (timeout > margin)
+ if (timeout > margin) {
interval = timeout - margin;
- else
+ } else {
interval = 1;
+ }
ERROR("watchdogd: Adjusted interval to timeout returned by driver: timeout %d, interval %d, margin %d\n",
timeout, interval, margin);
}
}
- while(1) {
+ while (true) {
write(fd, "", 1);
sleep(interval);
}
}
-
diff --git a/libbacktrace/Android.build.mk b/libbacktrace/Android.build.mk
index 7e1cd53..35fed6d 100644
--- a/libbacktrace/Android.build.mk
+++ b/libbacktrace/Android.build.mk
@@ -19,28 +19,37 @@
LOCAL_MODULE := $(module)
LOCAL_MODULE_TAGS := $(module_tag)
LOCAL_MULTILIB := $($(module)_multilib)
+ifeq ($(LOCAL_MULTILIB),both)
+ifneq ($(build_target),$(filter $(build_target),SHARED_LIBRARY STATIC_LIBRRARY))
+ LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+ LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+endif
+endif
LOCAL_ADDITIONAL_DEPENDENCIES := \
$(LOCAL_PATH)/Android.mk \
$(LOCAL_PATH)/Android.build.mk \
LOCAL_CFLAGS := \
- $(common_cflags) \
+ $(libbacktrace_common_cflags) \
$($(module)_cflags) \
$($(module)_cflags_$(build_type)) \
+LOCAL_CLANG_CFLAGS += \
+ $(libbacktrace_common_clang_cflags) \
+
LOCAL_CONLYFLAGS += \
- $(common_conlyflags) \
+ $(libbacktrace_common_conlyflags) \
$($(module)_conlyflags) \
$($(module)_conlyflags_$(build_type)) \
LOCAL_CPPFLAGS += \
- $(common_cppflags) \
+ $(libbacktrace_common_cppflags) \
$($(module)_cppflags) \
$($(module)_cppflags_$(build_type)) \
LOCAL_C_INCLUDES := \
- $(common_c_includes) \
+ $(libbacktrace_common_c_includes) \
$($(module)_c_includes) \
$($(module)_c_includes_$(build_type)) \
@@ -67,10 +76,7 @@
ifeq ($(build_type),host)
# Only build if host builds are supported.
ifeq ($(build_host),true)
- LOCAL_CFLAGS += -Wno-extern-c-compat
- ifneq ($($(module)_libc++),)
- include external/libcxx/libcxx.mk
- endif
+ LOCAL_CFLAGS += -Wno-extern-c-compat -fno-omit-frame-pointer
include $(BUILD_HOST_$(build_target))
endif
endif
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
old mode 100755
new mode 100644
index f3b28dd..a2b4cfe
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -16,16 +16,20 @@
LOCAL_PATH:= $(call my-dir)
-common_cflags := \
+libbacktrace_common_cflags := \
-Wall \
-Werror \
-common_conlyflags := \
+libbacktrace_common_conlyflags := \
-std=gnu99 \
-common_cppflags := \
+libbacktrace_common_cppflags := \
-std=gnu++11 \
+# The latest clang (r230699) does not allow SP/PC to be declared in inline asm lists.
+libbacktrace_common_clang_cflags += \
+ -Wno-inline-asm
+
build_host := false
ifeq ($(HOST_OS),linux)
ifeq ($(HOST_ARCH),$(filter $(HOST_ARCH),x86 x86_64))
@@ -37,28 +41,20 @@
# The libbacktrace library.
#-------------------------------------------------------------------------
libbacktrace_src_files := \
- BacktraceImpl.cpp \
+ Backtrace.cpp \
+ BacktraceCurrent.cpp \
BacktraceMap.cpp \
- BacktraceThread.cpp \
+ BacktracePtrace.cpp \
thread_utils.c \
-
-libbacktrace_shared_libraries_target := \
- libcutils \
-
-libbacktrace_src_files += \
+ ThreadEntry.cpp \
UnwindCurrent.cpp \
UnwindMap.cpp \
UnwindPtrace.cpp \
libbacktrace_shared_libraries := \
- libunwind \
- libunwind-ptrace \
-
-libbacktrace_shared_libraries_host := \
+ libbase \
liblog \
-
-libbacktrace_static_libraries_host := \
- libcutils \
+ libunwind \
libbacktrace_ldlibs_host := \
-lpthread \
@@ -86,6 +82,7 @@
module_tag := debug
build_type := target
build_target := SHARED_LIBRARY
+libbacktrace_test_multilib := both
include $(LOCAL_PATH)/Android.build.mk
build_type := host
include $(LOCAL_PATH)/Android.build.mk
@@ -124,6 +121,7 @@
module_tag := debug
build_type := target
build_target := NATIVE_TEST
+backtrace_test_multilib := both
include $(LOCAL_PATH)/Android.build.mk
build_type := host
include $(LOCAL_PATH)/Android.build.mk
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
new file mode 100644
index 0000000..4e4003e
--- /dev/null
+++ b/libbacktrace/Backtrace.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <ucontext.h>
+
+#include <string>
+
+#include <base/stringprintf.h>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include <cutils/threads.h>
+
+#include "BacktraceLog.h"
+#include "thread_utils.h"
+#include "UnwindCurrent.h"
+#include "UnwindPtrace.h"
+
+using android::base::StringPrintf;
+
+//-------------------------------------------------------------------------
+// Backtrace functions.
+//-------------------------------------------------------------------------
+Backtrace::Backtrace(pid_t pid, pid_t tid, BacktraceMap* map)
+ : pid_(pid), tid_(tid), map_(map), map_shared_(true) {
+ if (map_ == nullptr) {
+ map_ = BacktraceMap::Create(pid);
+ map_shared_ = false;
+ }
+}
+
+Backtrace::~Backtrace() {
+ if (map_ && !map_shared_) {
+ delete map_;
+ map_ = nullptr;
+ }
+}
+
+extern "C" char* __cxa_demangle(const char* mangled, char* buf, size_t* len,
+ int* status);
+
+std::string Backtrace::GetFunctionName(uintptr_t pc, uintptr_t* offset) {
+ std::string func_name = GetFunctionNameRaw(pc, offset);
+ if (!func_name.empty()) {
+#if defined(__APPLE__)
+ // Mac OS' __cxa_demangle demangles "f" as "float"; last tested on 10.7.
+ if (func_name[0] != '_') {
+ return func_name;
+ }
+#endif
+ char* name = __cxa_demangle(func_name.c_str(), 0, 0, 0);
+ if (name) {
+ func_name = name;
+ free(name);
+ }
+ }
+ return func_name;
+}
+
+bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, word_t* out_value) {
+ if (ptr & (sizeof(word_t)-1)) {
+ BACK_LOGW("invalid pointer %p", reinterpret_cast<void*>(ptr));
+ *out_value = static_cast<word_t>(-1);
+ return false;
+ }
+ return true;
+}
+
+std::string Backtrace::FormatFrameData(size_t frame_num) {
+ if (frame_num >= frames_.size()) {
+ return "";
+ }
+ return FormatFrameData(&frames_[frame_num]);
+}
+
+std::string Backtrace::FormatFrameData(const backtrace_frame_data_t* frame) {
+ const char* map_name;
+ if (BacktraceMap::IsValid(frame->map) && !frame->map.name.empty()) {
+ map_name = frame->map.name.c_str();
+ } else {
+ map_name = "<unknown>";
+ }
+
+ uintptr_t relative_pc = BacktraceMap::GetRelativePc(frame->map, frame->pc);
+
+ std::string line(StringPrintf("#%02zu pc %" PRIPTR " %s", frame->num, relative_pc, map_name));
+ if (!frame->func_name.empty()) {
+ line += " (" + frame->func_name;
+ if (frame->func_offset) {
+ line += StringPrintf("+%" PRIuPTR, frame->func_offset);
+ }
+ line += ')';
+ }
+
+ return line;
+}
+
+void Backtrace::FillInMap(uintptr_t pc, backtrace_map_t* map) {
+ map_->FillIn(pc, map);
+}
+
+Backtrace* Backtrace::Create(pid_t pid, pid_t tid, BacktraceMap* map) {
+ if (pid == BACKTRACE_CURRENT_PROCESS) {
+ pid = getpid();
+ if (tid == BACKTRACE_CURRENT_THREAD) {
+ tid = gettid();
+ }
+ } else if (tid == BACKTRACE_CURRENT_THREAD) {
+ tid = pid;
+ }
+
+ if (pid == getpid()) {
+ return new UnwindCurrent(pid, tid, map);
+ } else {
+ return new UnwindPtrace(pid, tid, map);
+ }
+}
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
new file mode 100644
index 0000000..14f04de
--- /dev/null
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE 1
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include <cutils/threads.h>
+
+#include "BacktraceCurrent.h"
+#include "BacktraceLog.h"
+#include "ThreadEntry.h"
+#include "thread_utils.h"
+
+bool BacktraceCurrent::ReadWord(uintptr_t ptr, word_t* out_value) {
+ if (!VerifyReadWordArgs(ptr, out_value)) {
+ return false;
+ }
+
+ backtrace_map_t map;
+ FillInMap(ptr, &map);
+ if (BacktraceMap::IsValid(map) && map.flags & PROT_READ) {
+ *out_value = *reinterpret_cast<word_t*>(ptr);
+ return true;
+ } else {
+ BACK_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr));
+ *out_value = static_cast<word_t>(-1);
+ return false;
+ }
+}
+
+size_t BacktraceCurrent::Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
+ backtrace_map_t map;
+ FillInMap(addr, &map);
+ if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
+ return 0;
+ }
+ bytes = MIN(map.end - addr, bytes);
+ memcpy(buffer, reinterpret_cast<uint8_t*>(addr), bytes);
+ return bytes;
+}
+
+bool BacktraceCurrent::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
+ if (ucontext) {
+ return UnwindFromContext(num_ignore_frames, ucontext);
+ }
+
+ if (Tid() != gettid()) {
+ return UnwindThread(num_ignore_frames);
+ }
+
+ return UnwindFromContext(num_ignore_frames, nullptr);
+}
+
+bool BacktraceCurrent::DiscardFrame(const backtrace_frame_data_t& frame) {
+ if (BacktraceMap::IsValid(frame.map)) {
+ const std::string library = basename(frame.map.name.c_str());
+ if (library == "libunwind.so" || library == "libbacktrace.so") {
+ return true;
+ }
+ }
+ return false;
+}
+
+static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void SignalHandler(int, siginfo_t*, void* sigcontext) {
+ ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false);
+ if (!entry) {
+ BACK_LOGW("Unable to find pid %d tid %d information", getpid(), gettid());
+ return;
+ }
+
+ entry->CopyUcontextFromSigcontext(sigcontext);
+
+ // Indicate the ucontext is now valid.
+ entry->Wake();
+
+ // Pause the thread until the unwind is complete. This avoids having
+ // the thread run ahead causing problems.
+ // The number indicates that we are waiting for the second Wake() call
+ // overall which is made by the thread requesting an unwind.
+ entry->Wait(2);
+
+ ThreadEntry::Remove(entry);
+}
+
+bool BacktraceCurrent::UnwindThread(size_t num_ignore_frames) {
+ // Prevent multiple threads trying to set the trigger action on different
+ // threads at the same time.
+ pthread_mutex_lock(&g_sigaction_mutex);
+
+ ThreadEntry* entry = ThreadEntry::Get(Pid(), Tid());
+ entry->Lock();
+
+ struct sigaction act, oldact;
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = SignalHandler;
+ act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+ sigemptyset(&act.sa_mask);
+ if (sigaction(THREAD_SIGNAL, &act, &oldact) != 0) {
+ BACK_LOGW("sigaction failed %s", strerror(errno));
+ entry->Unlock();
+ ThreadEntry::Remove(entry);
+ pthread_mutex_unlock(&g_sigaction_mutex);
+ return false;
+ }
+
+ if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) {
+ BACK_LOGW("tgkill %d failed: %s", Tid(), strerror(errno));
+ sigaction(THREAD_SIGNAL, &oldact, nullptr);
+ entry->Unlock();
+ ThreadEntry::Remove(entry);
+ pthread_mutex_unlock(&g_sigaction_mutex);
+ return false;
+ }
+
+ // Wait for the thread to get the ucontext. The number indicates
+ // that we are waiting for the first Wake() call made by the thread.
+ entry->Wait(1);
+
+ // After the thread has received the signal, allow other unwinders to
+ // continue.
+ sigaction(THREAD_SIGNAL, &oldact, nullptr);
+ pthread_mutex_unlock(&g_sigaction_mutex);
+
+ bool unwind_done = UnwindFromContext(num_ignore_frames, entry->GetUcontext());
+
+ // Tell the signal handler to exit and release the entry.
+ entry->Wake();
+
+ return unwind_done;
+}
diff --git a/libbacktrace/BacktraceCurrent.h b/libbacktrace/BacktraceCurrent.h
new file mode 100644
index 0000000..8aad36d
--- /dev/null
+++ b/libbacktrace/BacktraceCurrent.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_CURRENT_H
+#define _LIBBACKTRACE_BACKTRACE_CURRENT_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <ucontext.h>
+
+#include <backtrace/Backtrace.h>
+
+// The signal used to cause a thread to dump the stack.
+#if defined(__GLIBC__)
+// In order to run the backtrace_tests on the host, we can't use
+// the internal real time signals used by GLIBC. To avoid this,
+// use SIGRTMIN for the signal to dump the stack.
+#define THREAD_SIGNAL SIGRTMIN
+#else
+#define THREAD_SIGNAL (__SIGRTMIN+1)
+#endif
+
+class BacktraceMap;
+
+class BacktraceCurrent : public Backtrace {
+public:
+ BacktraceCurrent(pid_t pid, pid_t tid, BacktraceMap* map) : Backtrace(pid, tid, map) {}
+ virtual ~BacktraceCurrent() {}
+
+ size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) override;
+
+ bool ReadWord(uintptr_t ptr, word_t* out_value) override;
+
+ bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext) override;
+
+protected:
+ bool DiscardFrame(const backtrace_frame_data_t& frame);
+
+private:
+ bool UnwindThread(size_t num_ignore_frames);
+
+ virtual bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) = 0;
+};
+
+#endif // _LIBBACKTRACE_BACKTRACE_CURRENT_H
diff --git a/libbacktrace/BacktraceImpl.cpp b/libbacktrace/BacktraceImpl.cpp
deleted file mode 100644
index 4650b6a..0000000
--- a/libbacktrace/BacktraceImpl.cpp
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/param.h>
-#include <sys/ptrace.h>
-#include <sys/types.h>
-#include <ucontext.h>
-#include <unistd.h>
-
-#include <string>
-
-#include <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
-
-#include "BacktraceImpl.h"
-#include "BacktraceLog.h"
-#include "thread_utils.h"
-
-//-------------------------------------------------------------------------
-// Backtrace functions.
-//-------------------------------------------------------------------------
-Backtrace::Backtrace(BacktraceImpl* impl, pid_t pid, BacktraceMap* map)
- : pid_(pid), tid_(-1), map_(map), map_shared_(true), impl_(impl) {
- impl_->SetParent(this);
-
- if (map_ == NULL) {
- map_ = BacktraceMap::Create(pid);
- map_shared_ = false;
- }
-}
-
-Backtrace::~Backtrace() {
- if (impl_) {
- delete impl_;
- impl_ = NULL;
- }
-
- if (map_ && !map_shared_) {
- delete map_;
- map_ = NULL;
- }
-}
-
-bool Backtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
- return impl_->Unwind(num_ignore_frames, ucontext);
-}
-
-extern "C" char* __cxa_demangle(const char* mangled, char* buf, size_t* len,
- int* status);
-
-std::string Backtrace::GetFunctionName(uintptr_t pc, uintptr_t* offset) {
- std::string func_name = impl_->GetFunctionNameRaw(pc, offset);
- if (!func_name.empty()) {
-#if defined(__APPLE__)
- // Mac OS' __cxa_demangle demangles "f" as "float"; last tested on 10.7.
- if (func_name[0] != '_') {
- return func_name;
- }
-#endif
- char* name = __cxa_demangle(func_name.c_str(), 0, 0, 0);
- if (name) {
- func_name = name;
- free(name);
- }
- }
- return func_name;
-}
-
-bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, word_t* out_value) {
- if (ptr & (sizeof(word_t)-1)) {
- BACK_LOGW("invalid pointer %p", (void*)ptr);
- *out_value = (word_t)-1;
- return false;
- }
- return true;
-}
-
-std::string Backtrace::FormatFrameData(size_t frame_num) {
- if (frame_num >= frames_.size()) {
- return "";
- }
- return FormatFrameData(&frames_[frame_num]);
-}
-
-std::string Backtrace::FormatFrameData(const backtrace_frame_data_t* frame) {
- const char* map_name;
- if (BacktraceMap::IsValid(frame->map) && !frame->map.name.empty()) {
- map_name = frame->map.name.c_str();
- } else {
- map_name = "<unknown>";
- }
-
- uintptr_t relative_pc;
- if (BacktraceMap::IsValid(frame->map)) {
- relative_pc = frame->pc - frame->map.start;
- } else {
- relative_pc = frame->pc;
- }
-
- char buf[512];
- if (!frame->func_name.empty() && frame->func_offset) {
- snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s (%s+%" PRIuPTR ")",
- frame->num, (int)sizeof(uintptr_t)*2, relative_pc, map_name,
- frame->func_name.c_str(), frame->func_offset);
- } else if (!frame->func_name.empty()) {
- snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s (%s)", frame->num,
- (int)sizeof(uintptr_t)*2, relative_pc, map_name, frame->func_name.c_str());
- } else {
- snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s", frame->num,
- (int)sizeof(uintptr_t)*2, relative_pc, map_name);
- }
-
- return buf;
-}
-
-void Backtrace::FillInMap(uintptr_t pc, backtrace_map_t* map) {
- map_->FillIn(pc, map);
-}
-
-//-------------------------------------------------------------------------
-// BacktraceCurrent functions.
-//-------------------------------------------------------------------------
-BacktraceCurrent::BacktraceCurrent(
- BacktraceImpl* impl, BacktraceMap* map) : Backtrace(impl, getpid(), map) {
-}
-
-BacktraceCurrent::~BacktraceCurrent() {
-}
-
-bool BacktraceCurrent::ReadWord(uintptr_t ptr, word_t* out_value) {
- if (!VerifyReadWordArgs(ptr, out_value)) {
- return false;
- }
-
- backtrace_map_t map;
- FillInMap(ptr, &map);
- if (BacktraceMap::IsValid(map) && map.flags & PROT_READ) {
- *out_value = *reinterpret_cast<word_t*>(ptr);
- return true;
- } else {
- BACK_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr));
- *out_value = static_cast<word_t>(-1);
- return false;
- }
-}
-
-size_t BacktraceCurrent::Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
- backtrace_map_t map;
- FillInMap(addr, &map);
- if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
- return 0;
- }
- bytes = MIN(map.end - addr, bytes);
- memcpy(buffer, reinterpret_cast<uint8_t*>(addr), bytes);
- return bytes;
-}
-
-//-------------------------------------------------------------------------
-// BacktracePtrace functions.
-//-------------------------------------------------------------------------
-BacktracePtrace::BacktracePtrace(
- BacktraceImpl* impl, pid_t pid, pid_t tid, BacktraceMap* map)
- : Backtrace(impl, pid, map) {
- tid_ = tid;
-}
-
-BacktracePtrace::~BacktracePtrace() {
-}
-
-#if !defined(__APPLE__)
-static bool PtraceRead(pid_t tid, uintptr_t addr, word_t* out_value) {
- // ptrace() returns -1 and sets errno when the operation fails.
- // To disambiguate -1 from a valid result, we clear errno beforehand.
- errno = 0;
- *out_value = ptrace(PTRACE_PEEKTEXT, tid, reinterpret_cast<void*>(addr), NULL);
- if (*out_value == static_cast<word_t>(-1) && errno) {
- BACK_LOGW("invalid pointer %p reading from tid %d, ptrace() strerror(errno)=%s",
- reinterpret_cast<void*>(addr), tid, strerror(errno));
- return false;
- }
- return true;
-}
-#endif
-
-bool BacktracePtrace::ReadWord(uintptr_t ptr, word_t* out_value) {
-#if defined(__APPLE__)
- BACK_LOGW("MacOS does not support reading from another pid.");
- return false;
-#else
- if (!VerifyReadWordArgs(ptr, out_value)) {
- return false;
- }
-
- backtrace_map_t map;
- FillInMap(ptr, &map);
- if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
- return false;
- }
-
- return PtraceRead(Tid(), ptr, out_value);
-#endif
-}
-
-size_t BacktracePtrace::Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
-#if defined(__APPLE__)
- BACK_LOGW("MacOS does not support reading from another pid.");
- return 0;
-#else
- backtrace_map_t map;
- FillInMap(addr, &map);
- if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
- return 0;
- }
-
- bytes = MIN(map.end - addr, bytes);
- size_t bytes_read = 0;
- word_t data_word;
- size_t align_bytes = addr & (sizeof(word_t) - 1);
- if (align_bytes != 0) {
- if (!PtraceRead(Tid(), addr & ~(sizeof(word_t) - 1), &data_word)) {
- return 0;
- }
- align_bytes = sizeof(word_t) - align_bytes;
- memcpy(buffer, reinterpret_cast<uint8_t*>(&data_word) + sizeof(word_t) - align_bytes,
- align_bytes);
- addr += align_bytes;
- buffer += align_bytes;
- bytes -= align_bytes;
- bytes_read += align_bytes;
- }
-
- size_t num_words = bytes / sizeof(word_t);
- for (size_t i = 0; i < num_words; i++) {
- if (!PtraceRead(Tid(), addr, &data_word)) {
- return bytes_read;
- }
- memcpy(buffer, &data_word, sizeof(word_t));
- buffer += sizeof(word_t);
- addr += sizeof(word_t);
- bytes_read += sizeof(word_t);
- }
-
- size_t left_over = bytes & (sizeof(word_t) - 1);
- if (left_over) {
- if (!PtraceRead(Tid(), addr, &data_word)) {
- return bytes_read;
- }
- memcpy(buffer, &data_word, left_over);
- bytes_read += left_over;
- }
- return bytes_read;
-#endif
-}
-
-Backtrace* Backtrace::Create(pid_t pid, pid_t tid, BacktraceMap* map) {
- if (pid == BACKTRACE_CURRENT_PROCESS || pid == getpid()) {
- if (tid == BACKTRACE_CURRENT_THREAD || tid == gettid()) {
- return CreateCurrentObj(map);
- } else {
- return CreateThreadObj(tid, map);
- }
- } else if (tid == BACKTRACE_CURRENT_THREAD) {
- return CreatePtraceObj(pid, pid, map);
- } else {
- return CreatePtraceObj(pid, tid, map);
- }
-}
diff --git a/libbacktrace/BacktraceImpl.h b/libbacktrace/BacktraceImpl.h
deleted file mode 100755
index 18c3cb5..0000000
--- a/libbacktrace/BacktraceImpl.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LIBBACKTRACE_BACKTRACE_IMPL_H
-#define _LIBBACKTRACE_BACKTRACE_IMPL_H
-
-#include <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
-
-#include <sys/types.h>
-
-class BacktraceImpl {
-public:
- virtual ~BacktraceImpl() { }
-
- virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext) = 0;
-
- // The name returned is not demangled, Backtrace::GetFunctionName()
- // takes care of demangling the name.
- virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) = 0;
-
- void SetParent(Backtrace* backtrace) { backtrace_obj_ = backtrace; }
-
- inline pid_t Pid() { return backtrace_obj_->Pid(); }
- inline pid_t Tid() { return backtrace_obj_->Tid(); }
-
- inline void FillInMap(uintptr_t addr, backtrace_map_t* map) {
- backtrace_obj_->FillInMap(addr, map);
- }
- inline std::string GetFunctionName(uintptr_t pc, uintptr_t* offset) {
- return backtrace_obj_->GetFunctionName(pc, offset);
- }
- inline BacktraceMap* GetMap() { return backtrace_obj_->GetMap(); }
-
-protected:
- inline std::vector<backtrace_frame_data_t>* GetFrames() { return &backtrace_obj_->frames_; }
-
- Backtrace* backtrace_obj_;
-};
-
-class BacktraceCurrent : public Backtrace {
-public:
- BacktraceCurrent(BacktraceImpl* impl, BacktraceMap* map);
- virtual ~BacktraceCurrent();
-
- size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes);
-
- bool ReadWord(uintptr_t ptr, word_t* out_value);
-};
-
-class BacktracePtrace : public Backtrace {
-public:
- BacktracePtrace(BacktraceImpl* impl, pid_t pid, pid_t tid, BacktraceMap* map);
- virtual ~BacktracePtrace();
-
- size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes);
-
- bool ReadWord(uintptr_t ptr, word_t* out_value);
-};
-
-Backtrace* CreateCurrentObj(BacktraceMap* map);
-Backtrace* CreatePtraceObj(pid_t pid, pid_t tid, BacktraceMap* map);
-Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map);
-
-#endif // _LIBBACKTRACE_BACKTRACE_IMPL_H
diff --git a/libbacktrace/BacktraceLog.h b/libbacktrace/BacktraceLog.h
old mode 100755
new mode 100644
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index 82a4085..ca47f67 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -15,18 +15,15 @@
*/
#include <ctype.h>
+#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
-#include <string>
-#include <vector>
-
#include <backtrace/backtrace_constants.h>
#include <backtrace/BacktraceMap.h>
#include <log/log.h>
#include "thread_utils.h"
-#include "BacktraceImpl.h"
BacktraceMap::BacktraceMap(pid_t pid) : pid_(pid) {
if (pid_ < 0) {
@@ -116,7 +113,7 @@
snprintf(path, sizeof(path), "/proc/%d/maps", pid_);
FILE* fp = fopen(path, "r");
#endif
- if (fp == NULL) {
+ if (fp == nullptr) {
return false;
}
@@ -138,11 +135,11 @@
#if defined(__APPLE__)
// Corkscrew and libunwind don't compile on the mac, so create a generic
// map object.
-BacktraceMap* BacktraceMap::Create(pid_t pid, bool uncached) {
+BacktraceMap* BacktraceMap::Create(pid_t pid, bool /*uncached*/) {
BacktraceMap* map = new BacktraceMap(pid);
if (!map->Build()) {
delete map;
- return NULL;
+ return nullptr;
}
return map;
}
diff --git a/libbacktrace/BacktracePtrace.cpp b/libbacktrace/BacktracePtrace.cpp
new file mode 100644
index 0000000..e10cce1
--- /dev/null
+++ b/libbacktrace/BacktracePtrace.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include "BacktraceLog.h"
+#include "BacktracePtrace.h"
+#include "thread_utils.h"
+
+#if !defined(__APPLE__)
+static bool PtraceRead(pid_t tid, uintptr_t addr, word_t* out_value) {
+ // ptrace() returns -1 and sets errno when the operation fails.
+ // To disambiguate -1 from a valid result, we clear errno beforehand.
+ errno = 0;
+ *out_value = ptrace(PTRACE_PEEKTEXT, tid, reinterpret_cast<void*>(addr), nullptr);
+ if (*out_value == static_cast<word_t>(-1) && errno) {
+ BACK_LOGW("invalid pointer %p reading from tid %d, ptrace() strerror(errno)=%s",
+ reinterpret_cast<void*>(addr), tid, strerror(errno));
+ return false;
+ }
+ return true;
+}
+#endif
+
+bool BacktracePtrace::ReadWord(uintptr_t ptr, word_t* out_value) {
+#if defined(__APPLE__)
+ BACK_LOGW("MacOS does not support reading from another pid.");
+ return false;
+#else
+ if (!VerifyReadWordArgs(ptr, out_value)) {
+ return false;
+ }
+
+ backtrace_map_t map;
+ FillInMap(ptr, &map);
+ if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
+ return false;
+ }
+
+ return PtraceRead(Tid(), ptr, out_value);
+#endif
+}
+
+size_t BacktracePtrace::Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
+#if defined(__APPLE__)
+ BACK_LOGW("MacOS does not support reading from another pid.");
+ return 0;
+#else
+ backtrace_map_t map;
+ FillInMap(addr, &map);
+ if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
+ return 0;
+ }
+
+ bytes = MIN(map.end - addr, bytes);
+ size_t bytes_read = 0;
+ word_t data_word;
+ size_t align_bytes = addr & (sizeof(word_t) - 1);
+ if (align_bytes != 0) {
+ if (!PtraceRead(Tid(), addr & ~(sizeof(word_t) - 1), &data_word)) {
+ return 0;
+ }
+ size_t copy_bytes = MIN(sizeof(word_t) - align_bytes, bytes);
+ memcpy(buffer, reinterpret_cast<uint8_t*>(&data_word) + align_bytes, copy_bytes);
+ addr += copy_bytes;
+ buffer += copy_bytes;
+ bytes -= copy_bytes;
+ bytes_read += copy_bytes;
+ }
+
+ size_t num_words = bytes / sizeof(word_t);
+ for (size_t i = 0; i < num_words; i++) {
+ if (!PtraceRead(Tid(), addr, &data_word)) {
+ return bytes_read;
+ }
+ memcpy(buffer, &data_word, sizeof(word_t));
+ buffer += sizeof(word_t);
+ addr += sizeof(word_t);
+ bytes_read += sizeof(word_t);
+ }
+
+ size_t left_over = bytes & (sizeof(word_t) - 1);
+ if (left_over) {
+ if (!PtraceRead(Tid(), addr, &data_word)) {
+ return bytes_read;
+ }
+ memcpy(buffer, &data_word, left_over);
+ bytes_read += left_over;
+ }
+ return bytes_read;
+#endif
+}
diff --git a/libbacktrace/BacktracePtrace.h b/libbacktrace/BacktracePtrace.h
new file mode 100644
index 0000000..1d49811
--- /dev/null
+++ b/libbacktrace/BacktracePtrace.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_PTRACE_H
+#define _LIBBACKTRACE_BACKTRACE_PTRACE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <backtrace/Backtrace.h>
+
+class BacktraceMap;
+
+class BacktracePtrace : public Backtrace {
+public:
+ BacktracePtrace(pid_t pid, pid_t tid, BacktraceMap* map) : Backtrace(pid, tid, map) {}
+ virtual ~BacktracePtrace() {}
+
+ size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes);
+
+ bool ReadWord(uintptr_t ptr, word_t* out_value);
+};
+
+#endif // _LIBBACKTRACE_BACKTRACE_PTRACE_H
diff --git a/libbacktrace/BacktraceThread.cpp b/libbacktrace/BacktraceThread.cpp
deleted file mode 100644
index 439cc3b..0000000
--- a/libbacktrace/BacktraceThread.cpp
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/syscall.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <ucontext.h>
-#include <unistd.h>
-
-#include <cutils/atomic.h>
-
-#include "BacktraceLog.h"
-#include "BacktraceThread.h"
-#include "thread_utils.h"
-
-//-------------------------------------------------------------------------
-// ThreadEntry implementation.
-//-------------------------------------------------------------------------
-ThreadEntry* ThreadEntry::list_ = NULL;
-pthread_mutex_t ThreadEntry::list_mutex_ = PTHREAD_MUTEX_INITIALIZER;
-
-// Assumes that ThreadEntry::list_mutex_ has already been locked before
-// creating a ThreadEntry object.
-ThreadEntry::ThreadEntry(pid_t pid, pid_t tid)
- : pid_(pid), tid_(tid), ref_count_(1), mutex_(PTHREAD_MUTEX_INITIALIZER),
- wait_mutex_(PTHREAD_MUTEX_INITIALIZER), wait_value_(0),
- next_(ThreadEntry::list_), prev_(NULL) {
- pthread_condattr_t attr;
- pthread_condattr_init(&attr);
- pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
- pthread_cond_init(&wait_cond_, &attr);
-
- // Add ourselves to the list.
- if (ThreadEntry::list_) {
- ThreadEntry::list_->prev_ = this;
- }
- ThreadEntry::list_ = this;
-}
-
-ThreadEntry* ThreadEntry::Get(pid_t pid, pid_t tid, bool create) {
- pthread_mutex_lock(&ThreadEntry::list_mutex_);
- ThreadEntry* entry = list_;
- while (entry != NULL) {
- if (entry->Match(pid, tid)) {
- break;
- }
- entry = entry->next_;
- }
-
- if (!entry) {
- if (create) {
- entry = new ThreadEntry(pid, tid);
- }
- } else {
- entry->ref_count_++;
- }
- pthread_mutex_unlock(&ThreadEntry::list_mutex_);
-
- return entry;
-}
-
-void ThreadEntry::Remove(ThreadEntry* entry) {
- pthread_mutex_unlock(&entry->mutex_);
-
- pthread_mutex_lock(&ThreadEntry::list_mutex_);
- if (--entry->ref_count_ == 0) {
- delete entry;
- }
- pthread_mutex_unlock(&ThreadEntry::list_mutex_);
-}
-
-// Assumes that ThreadEntry::list_mutex_ has already been locked before
-// deleting a ThreadEntry object.
-ThreadEntry::~ThreadEntry() {
- if (list_ == this) {
- list_ = next_;
- } else {
- if (next_) {
- next_->prev_ = prev_;
- }
- prev_->next_ = next_;
- }
-
- next_ = NULL;
- prev_ = NULL;
-
- pthread_cond_destroy(&wait_cond_);
-}
-
-void ThreadEntry::Wait(int value) {
- timespec ts;
- if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) {
- BACK_LOGW("clock_gettime failed: %s", strerror(errno));
- abort();
- }
- ts.tv_sec += 10;
-
- pthread_mutex_lock(&wait_mutex_);
- while (wait_value_ != value) {
- int ret = pthread_cond_timedwait(&wait_cond_, &wait_mutex_, &ts);
- if (ret != 0) {
- BACK_LOGW("pthread_cond_timedwait failed: %s", strerror(ret));
- break;
- }
- }
- pthread_mutex_unlock(&wait_mutex_);
-}
-
-void ThreadEntry::Wake() {
- pthread_mutex_lock(&wait_mutex_);
- wait_value_++;
- pthread_mutex_unlock(&wait_mutex_);
-
- pthread_cond_signal(&wait_cond_);
-}
-
-void ThreadEntry::CopyUcontextFromSigcontext(void* sigcontext) {
- ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(sigcontext);
- // The only thing the unwinder cares about is the mcontext data.
- memcpy(&ucontext_.uc_mcontext, &ucontext->uc_mcontext, sizeof(ucontext->uc_mcontext));
-}
-
-//-------------------------------------------------------------------------
-// BacktraceThread functions.
-//-------------------------------------------------------------------------
-static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-static void SignalHandler(int, siginfo_t*, void* sigcontext) {
- ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false);
- if (!entry) {
- BACK_LOGW("Unable to find pid %d tid %d information", getpid(), gettid());
- return;
- }
-
- entry->CopyUcontextFromSigcontext(sigcontext);
-
- // Indicate the ucontext is now valid.
- entry->Wake();
-
- // Pause the thread until the unwind is complete. This avoids having
- // the thread run ahead causing problems.
- entry->Wait(2);
-
- ThreadEntry::Remove(entry);
-}
-
-BacktraceThread::BacktraceThread(BacktraceImpl* impl, pid_t tid, BacktraceMap* map)
- : BacktraceCurrent(impl, map) {
- tid_ = tid;
-}
-
-BacktraceThread::~BacktraceThread() {
-}
-
-bool BacktraceThread::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
- if (ucontext) {
- // Unwind using an already existing ucontext.
- return impl_->Unwind(num_ignore_frames, ucontext);
- }
-
- // Prevent multiple threads trying to set the trigger action on different
- // threads at the same time.
- if (pthread_mutex_lock(&g_sigaction_mutex) < 0) {
- BACK_LOGW("sigaction failed: %s", strerror(errno));
- return false;
- }
-
- ThreadEntry* entry = ThreadEntry::Get(Pid(), Tid());
- entry->Lock();
-
- struct sigaction act, oldact;
- memset(&act, 0, sizeof(act));
- act.sa_sigaction = SignalHandler;
- act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
- sigemptyset(&act.sa_mask);
- if (sigaction(THREAD_SIGNAL, &act, &oldact) != 0) {
- BACK_LOGW("sigaction failed %s", strerror(errno));
- entry->Unlock();
- ThreadEntry::Remove(entry);
- pthread_mutex_unlock(&g_sigaction_mutex);
- return false;
- }
-
- if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) {
- BACK_LOGW("tgkill %d failed: %s", Tid(), strerror(errno));
- sigaction(THREAD_SIGNAL, &oldact, NULL);
- entry->Unlock();
- ThreadEntry::Remove(entry);
- pthread_mutex_unlock(&g_sigaction_mutex);
- return false;
- }
-
- // Wait for the thread to get the ucontext.
- entry->Wait(1);
-
- // After the thread has received the signal, allow other unwinders to
- // continue.
- sigaction(THREAD_SIGNAL, &oldact, NULL);
- pthread_mutex_unlock(&g_sigaction_mutex);
-
- bool unwind_done = impl_->Unwind(num_ignore_frames, entry->GetUcontext());
-
- // Tell the signal handler to exit and release the entry.
- entry->Wake();
-
- return unwind_done;
-}
diff --git a/libbacktrace/GetPss.cpp b/libbacktrace/GetPss.cpp
index 442383b..09a721d 100644
--- a/libbacktrace/GetPss.cpp
+++ b/libbacktrace/GetPss.cpp
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-#include <assert.h>
#include <inttypes.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
-#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
@@ -46,13 +45,22 @@
size_t GetPssBytes() {
FILE* maps = fopen("/proc/self/maps", "r");
- assert(maps != NULL);
+ if (maps == nullptr) {
+ return 0;
+ }
int pagecount_fd = open("/proc/kpagecount", O_RDONLY);
- assert(pagecount_fd >= 0);
+ if (pagecount_fd == -1) {
+ fclose(maps);
+ return 0;
+ }
int pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
- assert(pagemap_fd >= 0);
+ if (pagemap_fd == -1) {
+ fclose(maps);
+ close(pagecount_fd);
+ return 0;
+ }
char line[4096];
size_t total_pss = 0;
diff --git a/libbacktrace/ThreadEntry.cpp b/libbacktrace/ThreadEntry.cpp
new file mode 100644
index 0000000..e8b60c8
--- /dev/null
+++ b/libbacktrace/ThreadEntry.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pthread.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#include <ucontext.h>
+
+#include "BacktraceLog.h"
+#include "ThreadEntry.h"
+
+// Initialize static member variables.
+ThreadEntry* ThreadEntry::list_ = nullptr;
+pthread_mutex_t ThreadEntry::list_mutex_ = PTHREAD_MUTEX_INITIALIZER;
+
+// Assumes that ThreadEntry::list_mutex_ has already been locked before
+// creating a ThreadEntry object.
+ThreadEntry::ThreadEntry(pid_t pid, pid_t tid)
+ : pid_(pid), tid_(tid), ref_count_(1), mutex_(PTHREAD_MUTEX_INITIALIZER),
+ wait_mutex_(PTHREAD_MUTEX_INITIALIZER), wait_value_(0),
+ next_(ThreadEntry::list_), prev_(nullptr) {
+ pthread_condattr_t attr;
+ pthread_condattr_init(&attr);
+ pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
+ pthread_cond_init(&wait_cond_, &attr);
+
+ // Add ourselves to the list.
+ if (ThreadEntry::list_) {
+ ThreadEntry::list_->prev_ = this;
+ }
+ ThreadEntry::list_ = this;
+}
+
+ThreadEntry* ThreadEntry::Get(pid_t pid, pid_t tid, bool create) {
+ pthread_mutex_lock(&ThreadEntry::list_mutex_);
+ ThreadEntry* entry = list_;
+ while (entry != nullptr) {
+ if (entry->Match(pid, tid)) {
+ break;
+ }
+ entry = entry->next_;
+ }
+
+ if (!entry) {
+ if (create) {
+ entry = new ThreadEntry(pid, tid);
+ }
+ } else {
+ entry->ref_count_++;
+ }
+ pthread_mutex_unlock(&ThreadEntry::list_mutex_);
+
+ return entry;
+}
+
+void ThreadEntry::Remove(ThreadEntry* entry) {
+ pthread_mutex_unlock(&entry->mutex_);
+
+ pthread_mutex_lock(&ThreadEntry::list_mutex_);
+ if (--entry->ref_count_ == 0) {
+ delete entry;
+ }
+ pthread_mutex_unlock(&ThreadEntry::list_mutex_);
+}
+
+// Assumes that ThreadEntry::list_mutex_ has already been locked before
+// deleting a ThreadEntry object.
+ThreadEntry::~ThreadEntry() {
+ if (list_ == this) {
+ list_ = next_;
+ } else {
+ if (next_) {
+ next_->prev_ = prev_;
+ }
+ prev_->next_ = next_;
+ }
+
+ next_ = nullptr;
+ prev_ = nullptr;
+
+ pthread_cond_destroy(&wait_cond_);
+}
+
+void ThreadEntry::Wait(int value) {
+ timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ ts.tv_sec += 10;
+
+ pthread_mutex_lock(&wait_mutex_);
+ while (wait_value_ != value) {
+ int ret = pthread_cond_timedwait(&wait_cond_, &wait_mutex_, &ts);
+ if (ret != 0) {
+ BACK_LOGW("pthread_cond_timedwait failed: %s", strerror(ret));
+ break;
+ }
+ }
+ pthread_mutex_unlock(&wait_mutex_);
+}
+
+void ThreadEntry::Wake() {
+ pthread_mutex_lock(&wait_mutex_);
+ wait_value_++;
+ pthread_mutex_unlock(&wait_mutex_);
+
+ pthread_cond_signal(&wait_cond_);
+}
+
+void ThreadEntry::CopyUcontextFromSigcontext(void* sigcontext) {
+ ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(sigcontext);
+ // The only thing the unwinder cares about is the mcontext data.
+ memcpy(&ucontext_.uc_mcontext, &ucontext->uc_mcontext, sizeof(ucontext->uc_mcontext));
+}
diff --git a/libbacktrace/BacktraceThread.h b/libbacktrace/ThreadEntry.h
similarity index 70%
rename from libbacktrace/BacktraceThread.h
rename to libbacktrace/ThreadEntry.h
index 99a8638..94becf2 100644
--- a/libbacktrace/BacktraceThread.h
+++ b/libbacktrace/ThreadEntry.h
@@ -14,26 +14,13 @@
* limitations under the License.
*/
-#ifndef _LIBBACKTRACE_BACKTRACE_THREAD_H
-#define _LIBBACKTRACE_BACKTRACE_THREAD_H
+#ifndef _LIBBACKTRACE_THREAD_ENTRY_H
+#define _LIBBACKTRACE_THREAD_ENTRY_H
-#include <inttypes.h>
#include <pthread.h>
-#include <signal.h>
-#include <string.h>
#include <sys/types.h>
#include <ucontext.h>
-#include "BacktraceImpl.h"
-
-// The signal used to cause a thread to dump the stack.
-#if defined(__GLIBC__)
-// GLIBC reserves __SIGRTMIN signals, so use SIGRTMIN to avoid errors.
-#define THREAD_SIGNAL SIGRTMIN
-#else
-#define THREAD_SIGNAL (__SIGRTMIN+1)
-#endif
-
class ThreadEntry {
public:
static ThreadEntry* Get(pid_t pid, pid_t tid, bool create = true);
@@ -81,12 +68,4 @@
static pthread_mutex_t list_mutex_;
};
-class BacktraceThread : public BacktraceCurrent {
-public:
- BacktraceThread(BacktraceImpl* impl, pid_t tid, BacktraceMap* map);
- virtual ~BacktraceThread();
-
- virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext);
-};
-
-#endif // _LIBBACKTRACE_BACKTRACE_THREAD_H
+#endif // _LIBBACKTRACE_THREAD_ENTRY_H
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
old mode 100755
new mode 100644
index 372555b..67e583f
--- a/libbacktrace/UnwindCurrent.cpp
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -14,41 +14,30 @@
* limitations under the License.
*/
-#include <sys/types.h>
+#include <stdint.h>
#include <ucontext.h>
-#include <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
+#include <memory>
+#include <string>
#define UNW_LOCAL_ONLY
#include <libunwind.h>
+#include <backtrace/Backtrace.h>
+
#include "BacktraceLog.h"
-#include "BacktraceThread.h"
#include "UnwindCurrent.h"
-#include "UnwindMap.h"
-//-------------------------------------------------------------------------
-// UnwindCurrent functions.
-//-------------------------------------------------------------------------
-UnwindCurrent::UnwindCurrent() {
-}
-
-UnwindCurrent::~UnwindCurrent() {
-}
-
-bool UnwindCurrent::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
- if (!ucontext) {
- int ret = unw_getcontext(&context_);
- if (ret < 0) {
- BACK_LOGW("unw_getcontext failed %d", ret);
- return false;
- }
+std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
+ *offset = 0;
+ char buf[512];
+ unw_word_t value;
+ if (unw_get_proc_name_by_ip(unw_local_addr_space, pc, buf, sizeof(buf),
+ &value, &context_) >= 0 && buf[0] != '\0') {
+ *offset = static_cast<uintptr_t>(value);
+ return buf;
}
- else {
- GetUnwContextFromUcontext(ucontext);
- }
- return UnwindFromContext(num_ignore_frames, false);
+ return "";
}
void UnwindCurrent::GetUnwContextFromUcontext(const ucontext_t* ucontext) {
@@ -76,89 +65,67 @@
#endif
}
-std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
- *offset = 0;
- char buf[512];
- unw_word_t value;
- if (unw_get_proc_name_by_ip(unw_local_addr_space, pc, buf, sizeof(buf),
- &value, &context_) >= 0 && buf[0] != '\0') {
- *offset = static_cast<uintptr_t>(value);
- return buf;
- }
- return "";
-}
-
-bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool within_handler) {
- // The cursor structure is pretty large, do not put it on the stack.
- unw_cursor_t* cursor = new unw_cursor_t;
- int ret = unw_init_local(cursor, &context_);
- if (ret < 0) {
- if (!within_handler) {
- BACK_LOGW("unw_init_local failed %d", ret);
+bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) {
+ if (ucontext == nullptr) {
+ int ret = unw_getcontext(&context_);
+ if (ret < 0) {
+ BACK_LOGW("unw_getcontext failed %d", ret);
+ return false;
}
- delete cursor;
+ } else {
+ GetUnwContextFromUcontext(ucontext);
+ }
+
+ // The cursor structure is pretty large, do not put it on the stack.
+ std::unique_ptr<unw_cursor_t> cursor(new unw_cursor_t);
+ int ret = unw_init_local(cursor.get(), &context_);
+ if (ret < 0) {
+ BACK_LOGW("unw_init_local failed %d", ret);
return false;
}
- std::vector<backtrace_frame_data_t>* frames = GetFrames();
- frames->reserve(MAX_BACKTRACE_FRAMES);
size_t num_frames = 0;
do {
unw_word_t pc;
- ret = unw_get_reg(cursor, UNW_REG_IP, &pc);
+ ret = unw_get_reg(cursor.get(), UNW_REG_IP, &pc);
if (ret < 0) {
- if (!within_handler) {
- BACK_LOGW("Failed to read IP %d", ret);
- }
+ BACK_LOGW("Failed to read IP %d", ret);
break;
}
unw_word_t sp;
- ret = unw_get_reg(cursor, UNW_REG_SP, &sp);
+ ret = unw_get_reg(cursor.get(), UNW_REG_SP, &sp);
if (ret < 0) {
- if (!within_handler) {
- BACK_LOGW("Failed to read SP %d", ret);
- }
+ BACK_LOGW("Failed to read SP %d", ret);
break;
}
- if (num_ignore_frames == 0) {
- frames->resize(num_frames+1);
- backtrace_frame_data_t* frame = &frames->at(num_frames);
- frame->num = num_frames;
- frame->pc = static_cast<uintptr_t>(pc);
- frame->sp = static_cast<uintptr_t>(sp);
- frame->stack_size = 0;
+ frames_.resize(num_frames+1);
+ backtrace_frame_data_t* frame = &frames_.at(num_frames);
+ frame->num = num_frames;
+ frame->pc = static_cast<uintptr_t>(pc);
+ frame->sp = static_cast<uintptr_t>(sp);
+ frame->stack_size = 0;
- if (num_frames > 0) {
- // Set the stack size for the previous frame.
- backtrace_frame_data_t* prev = &frames->at(num_frames-1);
- prev->stack_size = frame->sp - prev->sp;
- }
-
- if (!within_handler) {
+ FillInMap(frame->pc, &frame->map);
+ // Check to see if we should skip this frame because it's coming
+ // from within the library, and we are doing a local unwind.
+ if (ucontext != nullptr || num_frames != 0 || !DiscardFrame(*frame)) {
+ if (num_ignore_frames == 0) {
+ // GetFunctionName is an expensive call, only do it if we are
+ // keeping the frame.
frame->func_name = GetFunctionName(frame->pc, &frame->func_offset);
- FillInMap(frame->pc, &frame->map);
+ if (num_frames > 0) {
+ // Set the stack size for the previous frame.
+ backtrace_frame_data_t* prev = &frames_.at(num_frames-1);
+ prev->stack_size = frame->sp - prev->sp;
+ }
+ num_frames++;
} else {
- frame->func_offset = 0;
+ num_ignore_frames--;
}
- num_frames++;
- } else {
- num_ignore_frames--;
}
- ret = unw_step (cursor);
+ ret = unw_step (cursor.get());
} while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
- delete cursor;
return true;
}
-
-//-------------------------------------------------------------------------
-// C++ object creation function.
-//-------------------------------------------------------------------------
-Backtrace* CreateCurrentObj(BacktraceMap* map) {
- return new BacktraceCurrent(new UnwindCurrent(), map);
-}
-
-Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map) {
- return new BacktraceThread(new UnwindCurrent(), tid, map);
-}
diff --git a/libbacktrace/UnwindCurrent.h b/libbacktrace/UnwindCurrent.h
index 2375e6e..3023996 100644
--- a/libbacktrace/UnwindCurrent.h
+++ b/libbacktrace/UnwindCurrent.h
@@ -17,27 +17,32 @@
#ifndef _LIBBACKTRACE_UNWIND_CURRENT_H
#define _LIBBACKTRACE_UNWIND_CURRENT_H
+#include <stdint.h>
+#include <sys/types.h>
+#include <ucontext.h>
+
#include <string>
-#include "BacktraceImpl.h"
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include "BacktraceCurrent.h"
#define UNW_LOCAL_ONLY
#include <libunwind.h>
-class UnwindCurrent : public BacktraceImpl {
+class UnwindCurrent : public BacktraceCurrent {
public:
- UnwindCurrent();
- virtual ~UnwindCurrent();
+ UnwindCurrent(pid_t pid, pid_t tid, BacktraceMap* map) : BacktraceCurrent(pid, tid, map) {}
+ virtual ~UnwindCurrent() {}
- virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext);
+ std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
- virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
+private:
+ void GetUnwContextFromUcontext(const ucontext_t* ucontext);
- bool UnwindFromContext(size_t num_ignore_frames, bool within_handler);
+ bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) override;
- void GetUnwContextFromUcontext(const ucontext_t* context);
-
-protected:
unw_context_t context_;
};
diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp
index 284a561..879fea5 100644
--- a/libbacktrace/UnwindMap.cpp
+++ b/libbacktrace/UnwindMap.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <pthread.h>
+#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
@@ -51,6 +51,8 @@
map.start = unw_map.start;
map.end = unw_map.end;
+ map.offset = unw_map.offset;
+ map.load_base = unw_map.load_base;
map.flags = unw_map.flags;
map.name = unw_map.path;
@@ -91,6 +93,8 @@
map.start = unw_map.start;
map.end = unw_map.end;
+ map.offset = unw_map.offset;
+ map.load_base = unw_map.load_base;
map.flags = unw_map.flags;
map.name = unw_map.path;
@@ -142,7 +146,7 @@
}
if (!map->Build()) {
delete map;
- return NULL;
+ return nullptr;
}
return map;
}
diff --git a/libbacktrace/UnwindMap.h b/libbacktrace/UnwindMap.h
index be8855e..e292016 100644
--- a/libbacktrace/UnwindMap.h
+++ b/libbacktrace/UnwindMap.h
@@ -17,6 +17,9 @@
#ifndef _LIBBACKTRACE_UNWIND_MAP_H
#define _LIBBACKTRACE_UNWIND_MAP_H
+#include <stdint.h>
+#include <sys/types.h>
+
#include <backtrace/BacktraceMap.h>
// The unw_map_cursor_t structure is different depending on whether it is
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp
index efe758b..a7c3de5 100644
--- a/libbacktrace/UnwindPtrace.cpp
+++ b/libbacktrace/UnwindPtrace.cpp
@@ -14,35 +14,36 @@
* limitations under the License.
*/
-#include <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
-
+#include <stdint.h>
#include <sys/types.h>
-#include <string.h>
#include <ucontext.h>
#include <libunwind.h>
#include <libunwind-ptrace.h>
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
#include "BacktraceLog.h"
#include "UnwindMap.h"
#include "UnwindPtrace.h"
-UnwindPtrace::UnwindPtrace() : addr_space_(NULL), upt_info_(NULL) {
+UnwindPtrace::UnwindPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
+ : BacktracePtrace(pid, tid, map), addr_space_(nullptr), upt_info_(nullptr) {
}
UnwindPtrace::~UnwindPtrace() {
if (upt_info_) {
_UPT_destroy(upt_info_);
- upt_info_ = NULL;
+ upt_info_ = nullptr;
}
if (addr_space_) {
// Remove the map from the address space before destroying it.
// It will be freed in the UnwindMap destructor.
- unw_map_set(addr_space_, NULL);
+ unw_map_set(addr_space_, nullptr);
unw_destroy_addr_space(addr_space_);
- addr_space_ = NULL;
+ addr_space_ = nullptr;
}
}
@@ -74,8 +75,6 @@
return false;
}
- std::vector<backtrace_frame_data_t>* frames = GetFrames();
- frames->reserve(MAX_BACKTRACE_FRAMES);
size_t num_frames = 0;
do {
unw_word_t pc;
@@ -92,15 +91,15 @@
}
if (num_ignore_frames == 0) {
- frames->resize(num_frames+1);
- backtrace_frame_data_t* frame = &frames->at(num_frames);
+ frames_.resize(num_frames+1);
+ backtrace_frame_data_t* frame = &frames_.at(num_frames);
frame->num = num_frames;
frame->pc = static_cast<uintptr_t>(pc);
frame->sp = static_cast<uintptr_t>(sp);
frame->stack_size = 0;
if (num_frames > 0) {
- backtrace_frame_data_t* prev = &frames->at(num_frames-1);
+ backtrace_frame_data_t* prev = &frames_.at(num_frames-1);
prev->stack_size = frame->sp - prev->sp;
}
@@ -129,10 +128,3 @@
}
return "";
}
-
-//-------------------------------------------------------------------------
-// C++ object creation function.
-//-------------------------------------------------------------------------
-Backtrace* CreatePtraceObj(pid_t pid, pid_t tid, BacktraceMap* map) {
- return new BacktracePtrace(new UnwindPtrace(), pid, tid, map);
-}
diff --git a/libbacktrace/UnwindPtrace.h b/libbacktrace/UnwindPtrace.h
index 2fb7967..ab04abf 100644
--- a/libbacktrace/UnwindPtrace.h
+++ b/libbacktrace/UnwindPtrace.h
@@ -17,20 +17,26 @@
#ifndef _LIBBACKTRACE_UNWIND_PTRACE_H
#define _LIBBACKTRACE_UNWIND_PTRACE_H
+#include <stdint.h>
+#include <sys/types.h>
+
#include <string>
-#include "BacktraceImpl.h"
-
+#ifdef UNW_LOCAL_ONLY
+#undef UNW_LOCAL_ONLY
+#endif
#include <libunwind.h>
-class UnwindPtrace : public BacktraceImpl {
+#include "BacktracePtrace.h"
+
+class UnwindPtrace : public BacktracePtrace {
public:
- UnwindPtrace();
+ UnwindPtrace(pid_t pid, pid_t tid, BacktraceMap* map);
virtual ~UnwindPtrace();
- virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext);
+ bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext) override;
- virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
+ std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
private:
unw_addr_space_t addr_space_;
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index b1e34bd..a086547 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#define _GNU_SOURCE 1
#include <dirent.h>
#include <errno.h>
#include <inttypes.h>
@@ -29,19 +30,21 @@
#include <time.h>
#include <unistd.h>
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <vector>
+
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
-// For the THREAD_SIGNAL definition.
-#include "BacktraceThread.h"
-
#include <cutils/atomic.h>
+#include <cutils/threads.h>
+
#include <gtest/gtest.h>
-#include <algorithm>
-#include <memory>
-#include <vector>
-
+// For the THREAD_SIGNAL definition.
+#include "BacktraceCurrent.h"
#include "thread_utils.h"
// Number of microseconds per milliseconds.
@@ -83,15 +86,16 @@
return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec);
}
-void DumpFrames(Backtrace* backtrace) {
+std::string DumpFrames(Backtrace* backtrace) {
if (backtrace->NumFrames() == 0) {
- printf(" No frames to dump\n");
- return;
+ return " No frames to dump.\n";
}
+ std::string frame;
for (size_t i = 0; i < backtrace->NumFrames(); i++) {
- printf(" %s\n", backtrace->FormatFrameData(i).c_str());
+ frame += " " + backtrace->FormatFrameData(i) + '\n';
}
+ return frame;
}
void WaitForStop(pid_t pid) {
@@ -121,8 +125,10 @@
}
void VerifyLevelDump(Backtrace* backtrace) {
- ASSERT_GT(backtrace->NumFrames(), static_cast<size_t>(0));
- ASSERT_LT(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES));
+ ASSERT_GT(backtrace->NumFrames(), static_cast<size_t>(0))
+ << DumpFrames(backtrace);
+ ASSERT_LT(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
+ << DumpFrames(backtrace);
// Look through the frames starting at the highest to find the
// frame we want.
@@ -133,13 +139,17 @@
break;
}
}
- ASSERT_LT(static_cast<size_t>(0), frame_num);
- ASSERT_LE(static_cast<size_t>(3), frame_num);
+ ASSERT_LT(static_cast<size_t>(0), frame_num) << DumpFrames(backtrace);
+ ASSERT_LE(static_cast<size_t>(3), frame_num) << DumpFrames(backtrace);
- ASSERT_EQ(backtrace->GetFrame(frame_num)->func_name, "test_level_one");
- ASSERT_EQ(backtrace->GetFrame(frame_num-1)->func_name, "test_level_two");
- ASSERT_EQ(backtrace->GetFrame(frame_num-2)->func_name, "test_level_three");
- ASSERT_EQ(backtrace->GetFrame(frame_num-3)->func_name, "test_level_four");
+ ASSERT_EQ(backtrace->GetFrame(frame_num)->func_name, "test_level_one")
+ << DumpFrames(backtrace);
+ ASSERT_EQ(backtrace->GetFrame(frame_num-1)->func_name, "test_level_two")
+ << DumpFrames(backtrace);
+ ASSERT_EQ(backtrace->GetFrame(frame_num-2)->func_name, "test_level_three")
+ << DumpFrames(backtrace);
+ ASSERT_EQ(backtrace->GetFrame(frame_num-3)->func_name, "test_level_four")
+ << DumpFrames(backtrace);
}
void VerifyLevelBacktrace(void*) {
@@ -156,10 +166,11 @@
}
void VerifyMaxDump(Backtrace* backtrace) {
- ASSERT_EQ(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES));
+ ASSERT_EQ(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
+ << DumpFrames(backtrace);
// Verify that the last frame is our recursive call.
- ASSERT_EQ(backtrace->GetFrame(MAX_BACKTRACE_FRAMES-1)->func_name,
- "test_recursive_call");
+ ASSERT_EQ(backtrace->GetFrame(MAX_BACKTRACE_FRAMES-1)->func_name, "test_recursive_call")
+ << DumpFrames(backtrace);
}
void VerifyMaxBacktrace(void*) {
@@ -198,6 +209,24 @@
return false;
}
+TEST(libbacktrace, local_no_unwind_frames) {
+ // Verify that a local unwind does not include any frames within
+ // libunwind or libbacktrace.
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+
+ ASSERT_TRUE(backtrace->NumFrames() != 0);
+ for (const auto& frame : *backtrace ) {
+ if (BacktraceMap::IsValid(frame.map)) {
+ const std::string name = basename(frame.map.name.c_str());
+ ASSERT_TRUE(name != "libunwind.so" && name != "libbacktrace.so")
+ << DumpFrames(backtrace.get());
+ }
+ break;
+ }
+}
+
TEST(libbacktrace, local_trace) {
ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelBacktrace, nullptr), 0);
}
@@ -205,8 +234,10 @@
void VerifyIgnoreFrames(
Backtrace* bt_all, Backtrace* bt_ign1,
Backtrace* bt_ign2, const char* cur_proc) {
- EXPECT_EQ(bt_all->NumFrames(), bt_ign1->NumFrames() + 1);
- EXPECT_EQ(bt_all->NumFrames(), bt_ign2->NumFrames() + 2);
+ EXPECT_EQ(bt_all->NumFrames(), bt_ign1->NumFrames() + 1)
+ << "All backtrace:\n" << DumpFrames(bt_all) << "Ignore 1 backtrace:\n" << DumpFrames(bt_ign1);
+ EXPECT_EQ(bt_all->NumFrames(), bt_ign2->NumFrames() + 2)
+ << "All backtrace:\n" << DumpFrames(bt_all) << "Ignore 2 backtrace:\n" << DumpFrames(bt_ign2);
// Check all of the frames are the same > the current frame.
bool check = (cur_proc == nullptr);
@@ -264,6 +295,7 @@
}
uint64_t start = NanoTime();
bool verified = false;
+ std::string last_dump;
do {
usleep(US_PER_MSEC);
if (ptrace(PTRACE_ATTACH, ptrace_tid, 0, 0) == 0) {
@@ -275,18 +307,20 @@
map.reset(BacktraceMap::Create(pid));
}
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get()));
- ASSERT_TRUE(backtrace->Unwind(0));
ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
if (ReadyFunc(backtrace.get())) {
VerifyFunc(backtrace.get());
verified = true;
+ } else {
+ last_dump = DumpFrames(backtrace.get());
}
ASSERT_TRUE(ptrace(PTRACE_DETACH, ptrace_tid, 0, 0) == 0);
}
// If 5 seconds have passed, then we are done.
} while (!verified && (NanoTime() - start) <= 5 * NS_PER_SEC);
- ASSERT_TRUE(verified);
+ ASSERT_TRUE(verified) << "Last backtrace:\n" << last_dump;
}
TEST(libbacktrace, ptrace_trace) {
@@ -490,9 +524,13 @@
// The SA_RESTORER flag gets set behind our back, so a direct comparison
// doesn't work unless we mask the value off. Mips doesn't have this
// flag, so skip this on that platform.
-#ifdef SA_RESTORER
+#if defined(SA_RESTORER)
cur_action.sa_flags &= ~SA_RESTORER;
new_action.sa_flags &= ~SA_RESTORER;
+#elif defined(__GLIBC__)
+ // Our host compiler doesn't appear to define this flag for some reason.
+ cur_action.sa_flags &= ~0x04000000;
+ new_action.sa_flags &= ~0x04000000;
#endif
EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags);
}
@@ -674,16 +712,19 @@
BacktraceMap* map3 = BacktraceMap::Create(getpid());
Backtrace* back1 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map1);
+ ASSERT_TRUE(back1 != nullptr);
EXPECT_TRUE(back1->Unwind(0));
delete back1;
delete map1;
Backtrace* back2 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map2);
+ ASSERT_TRUE(back2 != nullptr);
EXPECT_TRUE(back2->Unwind(0));
delete back2;
delete map2;
Backtrace* back3 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map3);
+ ASSERT_TRUE(back3 != nullptr);
EXPECT_TRUE(back3->Unwind(0));
delete back3;
delete map3;
@@ -731,6 +772,7 @@
// Check map name empty, but exists.
frame.map.start = 1;
frame.map.end = 1;
+ frame.map.load_base = 0;
#if defined(__LP64__)
EXPECT_EQ("#01 pc 0000000000000001 <unknown>",
#else
@@ -768,6 +810,16 @@
EXPECT_EQ("#01 pc 12345678 MapFake (ProcFake+645)",
#endif
backtrace->FormatFrameData(&frame));
+
+ // Check func_name is set, func offset is non-zero, and load_base is non-zero.
+ frame.func_offset = 645;
+ frame.map.load_base = 100;
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 00000000123456dc MapFake (ProcFake+645)",
+#else
+ EXPECT_EQ("#01 pc 123456dc MapFake (ProcFake+645)",
+#endif
+ backtrace->FormatFrameData(&frame));
}
struct map_test_t {
@@ -831,6 +883,17 @@
ASSERT_EQ(waitpid(pid, nullptr, 0), pid);
}
+void InitMemory(uint8_t* memory, size_t bytes) {
+ for (size_t i = 0; i < bytes; i++) {
+ memory[i] = i;
+ if (memory[i] == '\0') {
+ // Don't use '\0' in our data so we can verify that an overread doesn't
+ // occur by using a '\0' as the character after the read data.
+ memory[i] = 23;
+ }
+ }
+}
+
void* ThreadReadTest(void* data) {
thread_t* thread_data = reinterpret_cast<thread_t*>(data);
@@ -849,19 +912,22 @@
}
// Set up a simple pattern in memory.
- for (size_t i = 0; i < pagesize; i++) {
- memory[i] = i;
- }
+ InitMemory(memory, pagesize);
thread_data->data = memory;
// Tell the caller it's okay to start reading memory.
android_atomic_acquire_store(1, &thread_data->state);
- // Loop waiting for everything
+ // Loop waiting for the caller to finish reading the memory.
while (thread_data->state) {
}
+ // Re-enable read-write on the page so that we don't crash if we try
+ // and access data on this page when freeing the memory.
+ if (mprotect(&memory[pagesize], pagesize, PROT_READ | PROT_WRITE) != 0) {
+ return reinterpret_cast<void*>(-1);
+ }
free(memory);
android_atomic_acquire_store(1, &thread_data->state);
@@ -874,9 +940,8 @@
// Create a page of data to use to do quick compares.
uint8_t* expected = new uint8_t[pagesize];
- for (size_t i = 0; i < pagesize; i++) {
- expected[i] = i;
- }
+ InitMemory(expected, pagesize);
+
uint8_t* data = new uint8_t[2*pagesize];
// Verify that we can only read one page worth of data.
size_t bytes_read = backtrace->Read(read_addr, data, 2 * pagesize);
@@ -890,6 +955,20 @@
ASSERT_TRUE(memcmp(data, &expected[i], 2 * sizeof(word_t)) == 0)
<< "Offset at " << i << " failed";
}
+
+ // Verify small unaligned reads.
+ for (size_t i = 1; i < sizeof(word_t); i++) {
+ for (size_t j = 1; j < sizeof(word_t); j++) {
+ // Set one byte past what we expect to read, to guarantee we don't overread.
+ data[j] = '\0';
+ bytes_read = backtrace->Read(read_addr + i, data, j);
+ ASSERT_EQ(j, bytes_read);
+ ASSERT_TRUE(memcmp(data, &expected[i], j) == 0)
+ << "Offset at " << i << " length " << j << " miscompared";
+ ASSERT_EQ('\0', data[j])
+ << "Offset at " << i << " length " << j << " wrote too much data";
+ }
+ }
delete data;
delete expected;
}
@@ -933,9 +1012,7 @@
}
// Set up a simple pattern in memory.
- for (size_t i = 0; i < pagesize; i++) {
- memory[i] = i;
- }
+ InitMemory(memory, pagesize);
g_addr = reinterpret_cast<uintptr_t>(memory);
g_ready = 1;
@@ -960,6 +1037,7 @@
WaitForStop(pid);
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
+ ASSERT_TRUE(backtrace.get() != nullptr);
uintptr_t read_addr;
size_t bytes_read = backtrace->Read(reinterpret_cast<uintptr_t>(&g_ready),
@@ -1005,6 +1083,7 @@
delete backtrace;
}
size_t stable_pss = GetPssBytes();
+ ASSERT_TRUE(stable_pss != 0);
// Loop enough that even a small leak should be detectable.
for (size_t i = 0; i < 4096; i++) {
@@ -1014,6 +1093,7 @@
delete backtrace;
}
size_t new_pss = GetPssBytes();
+ ASSERT_TRUE(new_pss != 0);
size_t abs_diff = (new_pss > stable_pss) ? new_pss - stable_pss : stable_pss - new_pss;
// As long as the new pss is within a certain amount, consider everything okay.
ASSERT_LE(abs_diff, MAX_LEAK_BYTES);
diff --git a/libbacktrace/thread_utils.c b/libbacktrace/thread_utils.c
index 6f4cd3c..e75f56e 100644
--- a/libbacktrace/thread_utils.c
+++ b/libbacktrace/thread_utils.c
@@ -16,25 +16,12 @@
#include "thread_utils.h"
-#if defined(__APPLE__)
+#if !defined(__BIONIC__)
-#include <sys/syscall.h>
-
-// Mac OS >= 10.6 has a system call equivalent to Linux's gettid().
-pid_t gettid() {
- return syscall(SYS_thread_selfid);
-}
-
-#elif !defined(__BIONIC__)
-
-// glibc doesn't implement or export either gettid or tgkill.
+// glibc doesn't implement or export tgkill.
#include <unistd.h>
#include <sys/syscall.h>
-pid_t gettid() {
- return syscall(__NR_gettid);
-}
-
int tgkill(int tgid, int tid, int sig) {
return syscall(__NR_tgkill, tgid, tid, sig);
}
diff --git a/libbacktrace/thread_utils.h b/libbacktrace/thread_utils.h
index ae4c929..df83581 100644
--- a/libbacktrace/thread_utils.h
+++ b/libbacktrace/thread_utils.h
@@ -23,8 +23,6 @@
int tgkill(int tgid, int tid, int sig);
-pid_t gettid();
-
__END_DECLS
#endif /* _LIBBACKTRACE_THREAD_UTILS_H */
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index 2c5e351..d5a9050 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -22,6 +22,7 @@
native_handle.c \
config_utils.c \
load_file.c \
+ strlcpy.c \
open_memstream.c \
strdup16to8.c \
strdup8to16.c \
@@ -31,6 +32,7 @@
sched_policy.c \
iosched_policy.c \
str_parms.c \
+ fs_config.c
# some files must not be compiled when building against Mingw
# they correspond to features not used by our host development tools
@@ -59,12 +61,13 @@
sockets.c \
commonHostSources += \
- ashmem-host.c
+ ashmem-host.c \
+ trace-host.c
endif
-# Static library for host
+# Shared and static library for host
# ========================================================
LOCAL_MODULE := libcutils
LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c
@@ -73,23 +76,18 @@
LOCAL_CFLAGS += -Werror
endif
LOCAL_MULTILIB := both
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_HOST_STATIC_LIBRARY)
-
-# Tests for host
-# ========================================================
include $(CLEAR_VARS)
-LOCAL_MODULE := tst_str_parms
-LOCAL_CFLAGS += -DTEST_STR_PARMS
+LOCAL_MODULE := libcutils
+LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c
+LOCAL_SHARED_LIBRARIES := liblog
ifneq ($(HOST_OS),windows)
LOCAL_CFLAGS += -Werror
endif
-LOCAL_SRC_FILES := str_parms.c hashmap.c memory.c
-LOCAL_STATIC_LIBRARIES := liblog
-LOCAL_MODULE_TAGS := optional
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-include $(BUILD_HOST_EXECUTABLE)
+LOCAL_MULTILIB := both
+include $(BUILD_HOST_SHARED_LIBRARY)
+
# Shared and static library for target
@@ -102,32 +100,17 @@
ashmem-dev.c \
debugger.c \
klog.c \
- memory.c \
partition_utils.c \
properties.c \
qtaguid.c \
- trace.c \
+ trace-dev.c \
uevent.c \
-LOCAL_SRC_FILES_arm += \
- arch-arm/memset32.S \
+LOCAL_SRC_FILES_arm += arch-arm/memset32.S
+LOCAL_SRC_FILES_arm64 += arch-arm64/android_memset.S
-# arch-arm/memset32.S does not compile with Clang.
-LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
-
-LOCAL_SRC_FILES_arm64 += \
- arch-arm64/android_memset.S \
-
-ifndef ARCH_MIPS_REV6
-LOCAL_SRC_FILES_mips += \
- arch-mips/android_memset.c \
-
-LOCAL_CFLAGS_mips += -DHAVE_MEMSET16 -DHAVE_MEMSET32
-endif
-
-# TODO: switch mips64 back to using arch-mips/android_memset.c
-LOCAL_SRC_FILES_mips64 += \
-# arch-mips/android_memset.c \
+LOCAL_SRC_FILES_mips += arch-mips/android_memset.c
+LOCAL_SRC_FILES_mips64 += arch-mips/android_memset.c
LOCAL_SRC_FILES_x86 += \
arch-x86/android_memset16.S \
@@ -137,16 +120,9 @@
arch-x86_64/android_memset16.S \
arch-x86_64/android_memset32.S \
-LOCAL_CFLAGS_arm += -DHAVE_MEMSET16 -DHAVE_MEMSET32
-LOCAL_CFLAGS_arm64 += -DHAVE_MEMSET16 -DHAVE_MEMSET32
-#LOCAL_CFLAGS_mips64 += -DHAVE_MEMSET16 -DHAVE_MEMSET32
-LOCAL_CFLAGS_x86 += -DHAVE_MEMSET16 -DHAVE_MEMSET32
-LOCAL_CFLAGS_x86_64 += -DHAVE_MEMSET16 -DHAVE_MEMSET32
-
LOCAL_C_INCLUDES := $(libcutils_c_includes)
LOCAL_STATIC_LIBRARIES := liblog
LOCAL_CFLAGS += -Werror -std=gnu90
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
@@ -157,16 +133,6 @@
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_CFLAGS += -Werror
LOCAL_C_INCLUDES := $(libcutils_c_includes)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_SHARED_LIBRARY)
-include $(CLEAR_VARS)
-LOCAL_MODULE := tst_str_parms
-LOCAL_CFLAGS += -DTEST_STR_PARMS -Werror
-LOCAL_SRC_FILES := str_parms.c hashmap.c memory.c
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_MODULE_TAGS := optional
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-include $(BUILD_EXECUTABLE)
-
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libcutils/arch-arm/memset32.S b/libcutils/arch-arm/memset32.S
index 6efab9f..1e89636 100644
--- a/libcutils/arch-arm/memset32.S
+++ b/libcutils/arch-arm/memset32.S
@@ -18,6 +18,8 @@
*
*/
+ .syntax unified
+
.text
.align
@@ -45,7 +47,7 @@
/* align to 32 bits */
tst r0, #2
- strneh r1, [r0], #2
+ strhne r1, [r0], #2
subne r2, r2, #2
.fnend
@@ -68,27 +70,27 @@
/* conditionally writes 0 to 7 words (length in r3) */
movs r3, r3, lsl #28
- stmcsia r0!, {r1, lr}
- stmcsia r0!, {r1, lr}
- stmmiia r0!, {r1, lr}
+ stmiacs r0!, {r1, lr}
+ stmiacs r0!, {r1, lr}
+ stmiami r0!, {r1, lr}
movs r3, r3, lsl #2
strcs r1, [r0], #4
.Laligned32:
mov r3, r1
1: subs r2, r2, #32
- stmhsia r0!, {r1,r3,r12,lr}
- stmhsia r0!, {r1,r3,r12,lr}
+ stmiahs r0!, {r1,r3,r12,lr}
+ stmiahs r0!, {r1,r3,r12,lr}
bhs 1b
add r2, r2, #32
/* conditionally stores 0 to 30 bytes */
movs r2, r2, lsl #28
- stmcsia r0!, {r1,r3,r12,lr}
- stmmiia r0!, {r1,lr}
+ stmiacs r0!, {r1,r3,r12,lr}
+ stmiami r0!, {r1,lr}
movs r2, r2, lsl #2
strcs r1, [r0], #4
- strmih lr, [r0], #2
+ strhmi lr, [r0], #2
ldr lr, [sp], #4
.cfi_def_cfa_offset 0
diff --git a/libcutils/arch-mips/android_memset.c b/libcutils/arch-mips/android_memset.c
index bbc99fe..a6b7496 100644
--- a/libcutils/arch-mips/android_memset.c
+++ b/libcutils/arch-mips/android_memset.c
@@ -1,31 +1,93 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
*
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
*/
+/* generic C version for any machine */
+
#include <cutils/memory.h>
-/* Use mips-assembler versions supplied by bionic/libc/arch-mips/string/memset.S: */
-void _memset16(uint16_t* dst, uint16_t value, size_t size);
-void _memset32(uint32_t* dst, uint32_t value, size_t size);
-
void android_memset16(uint16_t* dst, uint16_t value, size_t size)
{
- _memset16(dst, value, size);
+ /* optimized version of
+ size >>= 1;
+ while (size--)
+ *dst++ = value;
+ */
+
+ size >>= 1;
+ if (((uintptr_t)dst & 2) && size) {
+ /* fill unpaired first elem separately */
+ *dst++ = value;
+ size--;
+ }
+ /* dst is now 32-bit-aligned */
+ /* fill body with 32-bit pairs */
+ uint32_t value32 = (value << 16) | value;
+ android_memset32((uint32_t*) dst, value32, size<<1);
+ if (size & 1) {
+ dst[size-1] = value; /* fill unpaired last elem */
+ }
}
+
void android_memset32(uint32_t* dst, uint32_t value, size_t size)
{
- _memset32(dst, value, size);
+ /* optimized version of
+ size >>= 2;
+ while (size--)
+ *dst++ = value;
+ */
+
+ size >>= 2;
+ if (((uintptr_t)dst & 4) && size) {
+ /* fill unpaired first 32-bit elem separately */
+ *dst++ = value;
+ size--;
+ }
+ /* dst is now 64-bit aligned */
+ /* fill body with 64-bit pairs */
+ uint64_t value64 = (((uint64_t)value)<<32) | value;
+ uint64_t* dst64 = (uint64_t*)dst;
+
+ while (size >= 12) {
+ dst64[0] = value64;
+ dst64[1] = value64;
+ dst64[2] = value64;
+ dst64[3] = value64;
+ dst64[4] = value64;
+ dst64[5] = value64;
+ size -= 12;
+ dst64 += 6;
+ }
+
+ /* fill remainder with original 32-bit single-elem loop */
+ dst = (uint32_t*) dst64;
+ while (size--) {
+ *dst++ = value;
+ }
+
}
diff --git a/libcutils/dir_hash.c b/libcutils/dir_hash.c
deleted file mode 100644
index 098b5db..0000000
--- a/libcutils/dir_hash.c
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <dirent.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sha1.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include <sys/stat.h>
-
-#include <netinet/in.h>
-#include <resolv.h>
-
-#include <cutils/dir_hash.h>
-
-/**
- * Copies, if it fits within max_output_string bytes, into output_string
- * a hash of the contents, size, permissions, uid, and gid of the file
- * specified by path, using the specified algorithm. Returns the length
- * of the output string, or a negative number if the buffer is too short.
- */
-int get_file_hash(HashAlgorithm algorithm, const char *path,
- char *output_string, size_t max_output_string) {
- SHA1_CTX context;
- struct stat sb;
- unsigned char md[SHA1_DIGEST_LENGTH];
- int used;
- size_t n;
-
- if (algorithm != SHA_1) {
- errno = EINVAL;
- return -1;
- }
-
- if (stat(path, &sb) != 0) {
- return -1;
- }
-
- if (S_ISLNK(sb.st_mode)) {
- char buf[PATH_MAX];
- int len;
-
- len = readlink(path, buf, sizeof(buf));
- if (len < 0) {
- return -1;
- }
-
- SHA1Init(&context);
- SHA1Update(&context, (unsigned char *) buf, len);
- SHA1Final(md, &context);
- } else if (S_ISREG(sb.st_mode)) {
- char buf[10000];
- FILE *f = fopen(path, "rb");
- int len;
-
- if (f == NULL) {
- return -1;
- }
-
- SHA1Init(&context);
-
- while ((len = fread(buf, 1, sizeof(buf), f)) > 0) {
- SHA1Update(&context, (unsigned char *) buf, len);
- }
-
- if (ferror(f)) {
- fclose(f);
- return -1;
- }
-
- fclose(f);
- SHA1Final(md, &context);
- }
-
- if (S_ISLNK(sb.st_mode) || S_ISREG(sb.st_mode)) {
- used = b64_ntop(md, SHA1_DIGEST_LENGTH,
- output_string, max_output_string);
- if (used < 0) {
- errno = ENOSPC;
- return -1;
- }
-
- n = snprintf(output_string + used, max_output_string - used,
- " %d 0%o %d %d", (int) sb.st_size, sb.st_mode,
- (int) sb.st_uid, (int) sb.st_gid);
- } else {
- n = snprintf(output_string, max_output_string,
- "- - 0%o %d %d", sb.st_mode,
- (int) sb.st_uid, (int) sb.st_gid);
- }
-
- if (n >= max_output_string - used) {
- errno = ENOSPC;
- return -(used + n);
- }
-
- return used + n;
-}
-
-struct list {
- char *name;
- struct list *next;
-};
-
-static int cmp(const void *a, const void *b) {
- struct list *const *ra = a;
- struct list *const *rb = b;
-
- return strcmp((*ra)->name, (*rb)->name);
-}
-
-static int recurse(HashAlgorithm algorithm, const char *directory_path,
- struct list **out) {
- struct list *list = NULL;
- struct list *f;
-
- struct dirent *de;
- DIR *d = opendir(directory_path);
-
- if (d == NULL) {
- return -1;
- }
-
- while ((de = readdir(d)) != NULL) {
- if (strcmp(de->d_name, ".") == 0) {
- continue;
- }
- if (strcmp(de->d_name, "..") == 0) {
- continue;
- }
-
- char *name = malloc(strlen(de->d_name) + 1);
- struct list *node = malloc(sizeof(struct list));
-
- if (name == NULL || node == NULL) {
- struct list *next;
- for (f = list; f != NULL; f = next) {
- next = f->next;
- free(f->name);
- free(f);
- }
-
- free(name);
- free(node);
- closedir(d);
- return -1;
- }
-
- strcpy(name, de->d_name);
-
- node->name = name;
- node->next = list;
- list = node;
- }
-
- closedir(d);
-
- for (f = list; f != NULL; f = f->next) {
- struct stat sb;
- char *name;
- char outstr[NAME_MAX + 100];
- char *keep;
- struct list *res;
-
- name = malloc(strlen(f->name) + strlen(directory_path) + 2);
- if (name == NULL) {
- struct list *next;
- for (f = list; f != NULL; f = f->next) {
- next = f->next;
- free(f->name);
- free(f);
- }
- for (f = *out; f != NULL; f = f->next) {
- next = f->next;
- free(f->name);
- free(f);
- }
- *out = NULL;
- return -1;
- }
-
- sprintf(name, "%s/%s", directory_path, f->name);
-
- int len = get_file_hash(algorithm, name,
- outstr, sizeof(outstr));
- if (len < 0) {
- // should not happen
- return -1;
- }
-
- keep = malloc(len + strlen(name) + 3);
- res = malloc(sizeof(struct list));
-
- if (keep == NULL || res == NULL) {
- struct list *next;
- for (f = list; f != NULL; f = f->next) {
- next = f->next;
- free(f->name);
- free(f);
- }
- for (f = *out; f != NULL; f = f->next) {
- next = f->next;
- free(f->name);
- free(f);
- }
- *out = NULL;
-
- free(keep);
- free(res);
- return -1;
- }
-
- sprintf(keep, "%s %s\n", name, outstr);
-
- res->name = keep;
- res->next = *out;
- *out = res;
-
- if ((stat(name, &sb) == 0) && S_ISDIR(sb.st_mode)) {
- if (recurse(algorithm, name, out) < 0) {
- struct list *next;
- for (f = list; f != NULL; f = next) {
- next = f->next;
- free(f->name);
- free(f);
- }
-
- return -1;
- }
- }
- }
-
- struct list *next;
- for (f = list; f != NULL; f = next) {
- next = f->next;
-
- free(f->name);
- free(f);
- }
-}
-
-/**
- * Allocates a string containing the names and hashes of all files recursively
- * reached under the specified directory_path, using the specified algorithm.
- * The string is returned as *output_string; the return value is the length
- * of the string, or a negative number if there was a failure.
- */
-int get_recursive_hash_manifest(HashAlgorithm algorithm,
- const char *directory_path,
- char **output_string) {
- struct list *out = NULL;
- struct list *r;
- struct list **list;
- int count = 0;
- int len = 0;
- int retlen = 0;
- int i;
- char *buf;
-
- if (recurse(algorithm, directory_path, &out) < 0) {
- return -1;
- }
-
- for (r = out; r != NULL; r = r->next) {
- count++;
- len += strlen(r->name);
- }
-
- list = malloc(count * sizeof(struct list *));
- if (list == NULL) {
- struct list *next;
- for (r = out; r != NULL; r = next) {
- next = r->next;
- free(r->name);
- free(r);
- }
- return -1;
- }
-
- count = 0;
- for (r = out; r != NULL; r = r->next) {
- list[count++] = r;
- }
-
- qsort(list, count, sizeof(struct list *), cmp);
-
- buf = malloc(len + 1);
- if (buf == NULL) {
- struct list *next;
- for (r = out; r != NULL; r = next) {
- next = r->next;
- free(r->name);
- free(r);
- }
- free(list);
- return -1;
- }
-
- for (i = 0; i < count; i++) {
- int n = strlen(list[i]->name);
-
- strcpy(buf + retlen, list[i]->name);
- retlen += n;
- }
-
- free(list);
-
- struct list *next;
- for (r = out; r != NULL; r = next) {
- next = r->next;
-
- free(r->name);
- free(r);
- }
-
- *output_string = buf;
- return retlen;
-}
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
new file mode 100644
index 0000000..9f8023e
--- /dev/null
+++ b/libcutils/fs_config.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This file is used to define the properties of the filesystem
+** images generated by build tools (mkbootfs and mkyaffs2image) and
+** by the device side of adb.
+*/
+
+#define LOG_TAG "fs_config"
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <log/log.h>
+#include <private/android_filesystem_config.h>
+#include <utils/Compat.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/* The following structure is stored little endian */
+struct fs_path_config_from_file {
+ uint16_t len;
+ uint16_t mode;
+ uint16_t uid;
+ uint16_t gid;
+ uint64_t capabilities;
+ char prefix[];
+} __attribute__((__aligned__(sizeof(uint64_t))));
+
+/* My kingdom for <endian.h> */
+static inline uint16_t get2LE(const uint8_t* src)
+{
+ return src[0] | (src[1] << 8);
+}
+
+static inline uint64_t get8LE(const uint8_t* src)
+{
+ uint32_t low, high;
+
+ low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+ high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
+ return ((uint64_t) high << 32) | (uint64_t) low;
+}
+
+#define ALIGN(x, alignment) ( ((x) + ((alignment) - 1)) & ~((alignment) - 1) )
+
+/* Rules for directories.
+** These rules are applied based on "first match", so they
+** should start with the most specific path and work their
+** way up to the root.
+*/
+
+static const struct fs_path_config android_dirs[] = {
+ { 00770, AID_SYSTEM, AID_CACHE, 0, "cache" },
+ { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" },
+ { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private" },
+ { 00771, AID_ROOT, AID_ROOT, 0, "data/dalvik-cache" },
+ { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/data" },
+ { 00771, AID_SHELL, AID_SHELL, 0, "data/local/tmp" },
+ { 00771, AID_SHELL, AID_SHELL, 0, "data/local" },
+ { 01771, AID_SYSTEM, AID_MISC, 0, "data/misc" },
+ { 00770, AID_DHCP, AID_DHCP, 0, "data/misc/dhcp" },
+ { 00771, AID_SHARED_RELRO, AID_SHARED_RELRO, 0, "data/misc/shared_relro" },
+ { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media" },
+ { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/Music" },
+ { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "sbin" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/bin" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "system/etc/ppp" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "vendor" },
+ { 00777, AID_ROOT, AID_ROOT, 0, "sdcard" },
+ { 00755, AID_ROOT, AID_ROOT, 0, 0 },
+};
+
+/* Rules for files.
+** These rules are applied based on "first match", so they
+** should start with the most specific path and work their
+** way up to the root. Prefixes ending in * denotes wildcard
+** and will allow partial matches.
+*/
+static const char conf_dir[] = "/system/etc/fs_config_dirs";
+static const char conf_file[] = "/system/etc/fs_config_files";
+
+static const struct fs_path_config android_files[] = {
+ { 00440, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.rc" },
+ { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.sh" },
+ { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.ril" },
+ { 00550, AID_DHCP, AID_SHELL, 0, "system/etc/dhcpcd/dhcpcd-run-hooks" },
+ { 00555, AID_ROOT, AID_ROOT, 0, "system/etc/ppp/*" },
+ { 00555, AID_ROOT, AID_ROOT, 0, "system/etc/rc.*" },
+ { 00444, AID_ROOT, AID_ROOT, 0, conf_dir + 1 },
+ { 00444, AID_ROOT, AID_ROOT, 0, conf_file + 1 },
+ { 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app/*" },
+ { 00644, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/*" },
+ { 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
+ * 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" },
+
+ /* the following files have enhanced capabilities and ARE included in user builds. */
+ { 00750, AID_ROOT, AID_SHELL, (1ULL << CAP_SETUID) | (1ULL << CAP_SETGID), "system/bin/run-as" },
+ { 00700, AID_SYSTEM, AID_SHELL, (1ULL << CAP_BLOCK_SUSPEND), "system/bin/inputflinger" },
+
+ { 00750, AID_ROOT, AID_ROOT, 0, "system/bin/uncrypt" },
+ { 00750, AID_ROOT, AID_ROOT, 0, "system/bin/install-recovery.sh" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "system/lib64/valgrind/*" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor/bin/*" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "vendor/bin/*" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "sbin/*" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "bin/*" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "init*" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" },
+ { 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
+ { 00644, AID_ROOT, AID_ROOT, 0, 0 },
+};
+
+static int fs_config_open(int dir)
+{
+ int fd = -1;
+
+ const char *out = getenv("OUT");
+ if (out && *out) {
+ char *name = NULL;
+ asprintf(&name, "%s%s", out, dir ? conf_dir : conf_file);
+ if (name) {
+ fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_BINARY));
+ free(name);
+ }
+ }
+ if (fd < 0) {
+ fd = TEMP_FAILURE_RETRY(open(dir ? conf_dir : conf_file, O_RDONLY | O_BINARY));
+ }
+ return fd;
+}
+
+static bool fs_config_cmp(bool dir, const char *prefix, size_t len,
+ const char *path, size_t plen)
+{
+ if (dir) {
+ if (plen < len) {
+ return false;
+ }
+ } else {
+ /* If name ends in * then allow partial matches. */
+ if (prefix[len - 1] == '*') {
+ return !strncmp(prefix, path, len - 1);
+ }
+ if (plen != len) {
+ return false;
+ }
+ }
+ return !strncmp(prefix, path, len);
+}
+
+void fs_config(const char *path, int dir,
+ unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities)
+{
+ const struct fs_path_config *pc;
+ int fd, plen;
+
+ if (path[0] == '/') {
+ path++;
+ }
+
+ plen = strlen(path);
+
+ fd = fs_config_open(dir);
+ if (fd >= 0) {
+ struct fs_path_config_from_file header;
+
+ while (TEMP_FAILURE_RETRY(read(fd, &header, sizeof(header))) == sizeof(header)) {
+ char *prefix;
+ uint16_t host_len = get2LE((const uint8_t *)&header.len);
+ ssize_t len, remainder = host_len - sizeof(header);
+ if (remainder <= 0) {
+ ALOGE("%s len is corrupted", dir ? conf_dir : conf_file);
+ break;
+ }
+ prefix = calloc(1, remainder);
+ if (!prefix) {
+ ALOGE("%s out of memory", dir ? conf_dir : conf_file);
+ break;
+ }
+ if (TEMP_FAILURE_RETRY(read(fd, prefix, remainder)) != remainder) {
+ free(prefix);
+ ALOGE("%s prefix is truncated", dir ? conf_dir : conf_file);
+ break;
+ }
+ len = strnlen(prefix, remainder);
+ if (len >= remainder) { /* missing a terminating null */
+ free(prefix);
+ ALOGE("%s is corrupted", dir ? conf_dir : conf_file);
+ break;
+ }
+ if (fs_config_cmp(dir, prefix, len, path, plen)) {
+ free(prefix);
+ close(fd);
+ *uid = get2LE((const uint8_t *)&(header.uid));
+ *gid = get2LE((const uint8_t *)&(header.gid));
+ *mode = (*mode & (~07777)) | get2LE((const uint8_t *)&(header.mode));
+ *capabilities = get8LE((const uint8_t *)&(header.capabilities));
+ return;
+ }
+ free(prefix);
+ }
+ close(fd);
+ }
+
+ pc = dir ? android_dirs : android_files;
+ for(; pc->prefix; pc++){
+ if (fs_config_cmp(dir, pc->prefix, strlen(pc->prefix), path, plen)) {
+ break;
+ }
+ }
+ *uid = pc->uid;
+ *gid = pc->gid;
+ *mode = (*mode & (~07777)) | pc->mode;
+ *capabilities = pc->capabilities;
+}
+
+ssize_t fs_config_generate(char *buffer, size_t length, const struct fs_path_config *pc)
+{
+ struct fs_path_config_from_file *p = (struct fs_path_config_from_file *)buffer;
+ size_t len = ALIGN(sizeof(*p) + strlen(pc->prefix) + 1, sizeof(uint64_t));
+
+ if ((length < len) || (len > UINT16_MAX)) {
+ return -ENOSPC;
+ }
+ memset(p, 0, len);
+ uint16_t host_len = len;
+ p->len = get2LE((const uint8_t *)&host_len);
+ p->mode = get2LE((const uint8_t *)&(pc->mode));
+ p->uid = get2LE((const uint8_t *)&(pc->uid));
+ p->gid = get2LE((const uint8_t *)&(pc->gid));
+ p->capabilities = get8LE((const uint8_t *)&(pc->capabilities));
+ strcpy(p->prefix, pc->prefix);
+ return len;
+}
diff --git a/libcutils/iosched_policy.c b/libcutils/iosched_policy.c
index a6da9ca..8946d3c 100644
--- a/libcutils/iosched_policy.c
+++ b/libcutils/iosched_policy.c
@@ -1,5 +1,5 @@
/*
-** Copyright 2007-2014, The Android Open Source Project
+** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
diff --git a/libcutils/klog.c b/libcutils/klog.c
index fbb7b72..f574f08 100644
--- a/libcutils/klog.c
+++ b/libcutils/klog.c
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-#include <sys/stat.h>
-#include <sys/types.h>
+#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <unistd.h>
#include <cutils/klog.h>
@@ -36,41 +37,36 @@
klog_level = level;
}
-void klog_init(void)
-{
- static const char *name = "/dev/__kmsg__";
-
+void klog_init(void) {
if (klog_fd >= 0) return; /* Already initialized */
+ static const char* name = "/dev/__kmsg__";
if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {
- klog_fd = open(name, O_WRONLY);
- if (klog_fd < 0)
- return;
- fcntl(klog_fd, F_SETFD, FD_CLOEXEC);
+ klog_fd = open(name, O_WRONLY | O_CLOEXEC);
unlink(name);
}
}
#define LOG_BUF_MAX 512
-void klog_vwrite(int level, const char *fmt, va_list ap)
-{
- char buf[LOG_BUF_MAX];
-
+void klog_writev(int level, const struct iovec* iov, int iov_count) {
if (level > klog_level) return;
if (klog_fd < 0) klog_init();
if (klog_fd < 0) return;
-
- vsnprintf(buf, LOG_BUF_MAX, fmt, ap);
- buf[LOG_BUF_MAX - 1] = 0;
-
- write(klog_fd, buf, strlen(buf));
+ TEMP_FAILURE_RETRY(writev(klog_fd, iov, iov_count));
}
-void klog_write(int level, const char *fmt, ...)
-{
+void klog_write(int level, const char* fmt, ...) {
+ char buf[LOG_BUF_MAX];
va_list ap;
va_start(ap, fmt);
- klog_vwrite(level, fmt, ap);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
+
+ buf[LOG_BUF_MAX - 1] = 0;
+
+ struct iovec iov[1];
+ iov[0].iov_base = buf;
+ iov[0].iov_len = strlen(buf);
+ klog_writev(level, iov, 1);
}
diff --git a/libcutils/str_parms.c b/libcutils/str_parms.c
index dfe8c4b..924289a 100644
--- a/libcutils/str_parms.c
+++ b/libcutils/str_parms.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011-2013 The Android Open Source Project
+ * Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -357,51 +357,3 @@
{
hashmapForEach(str_parms->map, dump_entry, str_parms);
}
-
-#ifdef TEST_STR_PARMS
-static void test_str_parms_str(const char *str)
-{
- struct str_parms *str_parms;
- char *out_str;
-
- str_parms = str_parms_create_str(str);
- str_parms_add_str(str_parms, "dude", "woah");
- str_parms_add_str(str_parms, "dude", "woah");
- str_parms_del(str_parms, "dude");
- str_parms_dump(str_parms);
- out_str = str_parms_to_str(str_parms);
- str_parms_destroy(str_parms);
- ALOGI("%s: '%s' stringified is '%s'", __func__, str, out_str);
- free(out_str);
-}
-
-int main(void)
-{
- test_str_parms_str("");
- test_str_parms_str(";");
- test_str_parms_str("=");
- test_str_parms_str("=;");
- test_str_parms_str("=bar");
- test_str_parms_str("=bar;");
- test_str_parms_str("foo=");
- test_str_parms_str("foo=;");
- test_str_parms_str("foo=bar");
- test_str_parms_str("foo=bar;");
- test_str_parms_str("foo=bar;baz");
- test_str_parms_str("foo=bar;baz=");
- test_str_parms_str("foo=bar;baz=bat");
- test_str_parms_str("foo=bar;baz=bat;");
- test_str_parms_str("foo=bar;baz=bat;foo=bar");
-
- // hashmapPut reports errors by setting errno to ENOMEM.
- // Test that we're not confused by running in an environment where this is already true.
- errno = ENOMEM;
- test_str_parms_str("foo=bar;baz=");
- if (errno != ENOMEM) {
- abort();
- }
- test_str_parms_str("foo=bar;baz=");
-
- return 0;
-}
-#endif
diff --git a/libcutils/memory.c b/libcutils/strlcpy.c
similarity index 62%
rename from libcutils/memory.c
rename to libcutils/strlcpy.c
index 6486b45..c66246c 100644
--- a/libcutils/memory.c
+++ b/libcutils/strlcpy.c
@@ -1,43 +1,4 @@
/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <cutils/memory.h>
-
-#if !HAVE_MEMSET16
-void android_memset16(uint16_t* dst, uint16_t value, size_t size)
-{
- size >>= 1;
- while (size--) {
- *dst++ = value;
- }
-}
-#endif
-
-#if !HAVE_MEMSET32
-void android_memset32(uint32_t* dst, uint32_t value, size_t size)
-{
- size >>= 2;
- while (size--) {
- *dst++ = value;
- }
-}
-#endif
-
-#if !HAVE_STRLCPY
-/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -54,8 +15,13 @@
*/
#include <sys/types.h>
+
+#if defined(__GLIBC__) || defined(_WIN32)
+
#include <string.h>
+#include <cutils/memory.h>
+
/* Implementation of strlcpy() for platforms that don't already have it. */
/*
@@ -88,4 +54,5 @@
return(s - src - 1); /* count does not include NUL */
}
+
#endif
diff --git a/libcutils/tests/Android.mk b/libcutils/tests/Android.mk
index 5a54698..cf70345 100644
--- a/libcutils/tests/Android.mk
+++ b/libcutils/tests/Android.mk
@@ -15,38 +15,59 @@
LOCAL_PATH := $(call my-dir)
test_src_files := \
+ test_str_parms.cpp \
+
+test_target_only_src_files := \
MemsetTest.cpp \
PropertiesTest.cpp \
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_MODULE := libcutils_test
-LOCAL_SRC_FILES := $(test_src_files)
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- liblog \
- libutils \
+test_libraries := libcutils liblog
+
+#
+# Target.
+#
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils_test
+LOCAL_SRC_FILES := $(test_src_files) $(test_target_only_src_files)
+LOCAL_SHARED_LIBRARIES := $(test_libraries)
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
include $(BUILD_NATIVE_TEST)
-# The static libcutils tests cannot be built when using libc++ because there are
-# multiple symbol definition errors between libc++ and libgcc. b/18389856
-#include $(CLEAR_VARS)
-#LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-#LOCAL_MODULE := libcutils_test_static
-#LOCAL_FORCE_STATIC_EXECUTABLE := true
-#LOCAL_SRC_FILES := $(test_src_files)
-#LOCAL_STATIC_LIBRARIES := \
-# libc \
-# libcutils \
-# liblog \
-# libutils \
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils_test_static
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_SRC_FILES := $(test_src_files) $(test_target_only_src_files)
+LOCAL_STATIC_LIBRARIES := libc $(test_libraries)
+LOCAL_CXX_STL := libc++_static
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+include $(BUILD_NATIVE_TEST)
-#LOCAL_CXX_STL := stlport_static
-#LOCAL_MULTILIB := both
-#LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-#LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-#include $(BUILD_NATIVE_TEST)
+
+#
+# Host.
+#
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils_test
+LOCAL_SRC_FILES := $(test_src_files)
+LOCAL_SHARED_LIBRARIES := $(test_libraries)
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+include $(BUILD_HOST_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils_test_static
+LOCAL_SRC_FILES := $(test_src_files)
+LOCAL_STATIC_LIBRARIES := $(test_libraries)
+LOCAL_CXX_STL := libc++_static
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libcutils/tests/test_str_parms.cpp b/libcutils/tests/test_str_parms.cpp
new file mode 100644
index 0000000..d8f639b
--- /dev/null
+++ b/libcutils/tests/test_str_parms.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/str_parms.h>
+#include <gtest/gtest.h>
+
+static void test_str_parms_str(const char* str, const char* expected) {
+ str_parms* str_parms = str_parms_create_str(str);
+ str_parms_add_str(str_parms, "dude", "woah");
+ str_parms_add_str(str_parms, "dude", "woah");
+ str_parms_del(str_parms, "dude");
+ str_parms_dump(str_parms);
+ char* out_str = str_parms_to_str(str_parms);
+ str_parms_destroy(str_parms);
+ ASSERT_STREQ(expected, out_str) << str;
+ free(out_str);
+}
+
+TEST(str_parms, smoke) {
+ test_str_parms_str("", "");
+ test_str_parms_str(";", "");
+ test_str_parms_str("=", "");
+ test_str_parms_str("=;", "");
+ test_str_parms_str("=bar", "");
+ test_str_parms_str("=bar;", "");
+ test_str_parms_str("foo=", "foo=");
+ test_str_parms_str("foo=;", "foo=");
+ test_str_parms_str("foo=bar", "foo=bar");
+ test_str_parms_str("foo=bar;", "foo=bar");
+ test_str_parms_str("foo=bar;baz", "foo=bar;baz=");
+ test_str_parms_str("foo=bar;baz=", "foo=bar;baz=");
+ test_str_parms_str("foo=bar;baz=bat", "foo=bar;baz=bat");
+ test_str_parms_str("foo=bar;baz=bat;", "foo=bar;baz=bat");
+ test_str_parms_str("foo=bar1;baz=bat;foo=bar2", "foo=bar2;baz=bat");
+}
+
+TEST(str_parms, put_ENOMEM) {
+ // hashmapPut reports errors by setting errno to ENOMEM.
+ // Test that we're not confused by running in an environment where this is already true.
+ errno = ENOMEM;
+ test_str_parms_str("foo=bar;baz=", "foo=bar;baz=");
+ ASSERT_EQ(ENOMEM, errno);
+ test_str_parms_str("foo=bar;baz=", "foo=bar;baz=");
+}
diff --git a/libcutils/threads.c b/libcutils/threads.c
index ca600b3..036f8c5 100644
--- a/libcutils/threads.c
+++ b/libcutils/threads.c
@@ -14,9 +14,38 @@
** limitations under the License.
*/
-#include <cutils/threads.h>
+#include "cutils/threads.h"
+
+// For gettid.
+#if defined(__APPLE__)
+#include "AvailabilityMacros.h" // For MAC_OS_X_VERSION_MAX_ALLOWED
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <unistd.h>
+#elif defined(__linux__) && !defined(__ANDROID__)
+#include <syscall.h>
+#include <unistd.h>
+#elif defined(_WIN32)
+#include <windows.h>
+#endif
+
+// No definition needed for Android because we'll just pick up bionic's copy.
+#ifndef __ANDROID__
+pid_t gettid() {
+#if defined(__APPLE__)
+ return syscall(SYS_thread_selfid);
+#elif defined(__linux__)
+ return syscall(__NR_gettid);
+#elif defined(_WIN32)
+ return GetCurrentThreadId();
+#endif
+}
+#endif // __ANDROID__
#if !defined(_WIN32)
+
void* thread_store_get( thread_store_t* store )
{
if (!store->has_tls)
diff --git a/libcutils/trace.c b/libcutils/trace-dev.c
similarity index 93%
rename from libcutils/trace.c
rename to libcutils/trace-dev.c
index 4396625..a06987e 100644
--- a/libcutils/trace.c
+++ b/libcutils/trace-dev.c
@@ -18,11 +18,11 @@
#include <fcntl.h>
#include <limits.h>
#include <pthread.h>
+#include <stdatomic.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
-#include <cutils/atomic.h>
#include <cutils/compiler.h>
#include <cutils/properties.h>
#include <cutils/trace.h>
@@ -37,11 +37,11 @@
*/
#define ATRACE_MESSAGE_LENGTH 1024
-volatile int32_t atrace_is_ready = 0;
+atomic_bool atrace_is_ready = ATOMIC_VAR_INIT(false);
int atrace_marker_fd = -1;
uint64_t atrace_enabled_tags = ATRACE_TAG_NOT_READY;
static bool atrace_is_debuggable = false;
-static volatile int32_t atrace_is_enabled = 1;
+static atomic_bool atrace_is_enabled = ATOMIC_VAR_INIT(true);
static pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT;
static pthread_mutex_t atrace_tags_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -58,7 +58,7 @@
// the Zygote process from tracing.
void atrace_set_tracing_enabled(bool enabled)
{
- android_atomic_release_store(enabled ? 1 : 0, &atrace_is_enabled);
+ atomic_store_explicit(&atrace_is_enabled, enabled, memory_order_release);
atrace_update_tags();
}
@@ -155,8 +155,8 @@
void atrace_update_tags()
{
uint64_t tags;
- if (CC_UNLIKELY(android_atomic_acquire_load(&atrace_is_ready))) {
- if (android_atomic_acquire_load(&atrace_is_enabled)) {
+ if (CC_UNLIKELY(atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
+ if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
tags = atrace_get_property();
pthread_mutex_lock(&atrace_tags_mutex);
atrace_enabled_tags = tags;
@@ -183,7 +183,7 @@
atrace_enabled_tags = atrace_get_property();
done:
- android_atomic_release_store(1, &atrace_is_ready);
+ atomic_store_explicit(&atrace_is_ready, true, memory_order_release);
}
void atrace_setup()
diff --git a/libcutils/trace-host.c b/libcutils/trace-host.c
new file mode 100644
index 0000000..6478e3e
--- /dev/null
+++ b/libcutils/trace-host.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/trace.h>
+
+#ifndef __unused
+#define __unused __attribute__((__unused__))
+#endif
+
+atomic_bool atrace_is_ready = ATOMIC_VAR_INIT(true);
+int atrace_marker_fd = -1;
+uint64_t atrace_enabled_tags = 0;
+
+void atrace_set_debuggable(bool debuggable __unused) { }
+void atrace_set_tracing_enabled(bool enabled __unused) { }
+void atrace_update_tags() { }
+void atrace_setup() { }
+void atrace_begin_body(const char* name __unused) { }
+void atrace_async_begin_body(const char* name __unused, int32_t cookie __unused) { }
+void atrace_async_end_body(const char* name __unused, int32_t cookie __unused) { }
+void atrace_int_body(const char* name __unused, int32_t value __unused) { }
+void atrace_int64_body(const char* name __unused, int64_t value __unused) { }
diff --git a/libion/tests/Android.mk b/libion/tests/Android.mk
index abf527a..894f90e 100644
--- a/libion/tests/Android.mk
+++ b/libion/tests/Android.mk
@@ -18,7 +18,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE := ion-unit-tests
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
LOCAL_SHARED_LIBRARIES += libion
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../kernel-headers
diff --git a/liblog/Android.mk b/liblog/Android.mk
index 70aff83..d7766f5 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -85,7 +85,7 @@
LOCAL_CFLAGS := -Werror $(liblog_cflags)
# TODO: This is to work around b/19059885. Remove after root cause is fixed
-LOCAL_LDFLAGS_arm := -Wl,--hash-style=sysv
+LOCAL_LDFLAGS_arm := -Wl,--hash-style=both
include $(BUILD_SHARED_LIBRARY)
diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c
index df67123..2e09192 100644
--- a/liblog/log_is_loggable.c
+++ b/liblog/log_is_loggable.c
@@ -28,7 +28,7 @@
return def;
}
{
- static const char log_namespace[] = "log.tag.";
+ static const char log_namespace[] = "persist.log.tag.";
char key[sizeof(log_namespace) + strlen(tag)];
strcpy(key, log_namespace);
@@ -37,6 +37,9 @@
if (__system_property_get(key + 8, buf) <= 0) {
buf[0] = '\0';
}
+ if (!buf[0] && __system_property_get(key, buf) <= 0) {
+ buf[0] = '\0';
+ }
}
switch (toupper(buf[0])) {
case 'V': return ANDROID_LOG_VERBOSE;
@@ -53,17 +56,6 @@
int __android_log_is_loggable(int prio, const char *tag, int def)
{
- static char user;
- int logLevel;
-
- if (user == 0) {
- char buf[PROP_VALUE_MAX];
- if (__system_property_get("ro.build.type", buf) <= 0) {
- buf[0] = '\0';
- }
- user = strcmp(buf, "user") ? -1 : 1;
- }
-
- logLevel = (user == 1) ? def : __android_log_level(tag, def);
+ int logLevel = __android_log_level(tag, def);
return logLevel >= 0 && prio >= logLevel;
}
diff --git a/liblog/log_read.c b/liblog/log_read.c
index 5364e4f..9c4af30 100644
--- a/liblog/log_read.c
+++ b/liblog/log_read.c
@@ -208,6 +208,7 @@
[LOG_ID_EVENTS] = "events",
[LOG_ID_SYSTEM] = "system",
[LOG_ID_CRASH] = "crash",
+ [LOG_ID_KERNEL] = "kernel",
};
const char *android_log_id_to_name(log_id_t log_id)
diff --git a/liblog/log_read_kern.c b/liblog/log_read_kern.c
index bdc7b18..69b405c 100644
--- a/liblog/log_read_kern.c
+++ b/liblog/log_read_kern.c
@@ -62,7 +62,8 @@
[LOG_ID_RADIO] = "radio",
[LOG_ID_EVENTS] = "events",
[LOG_ID_SYSTEM] = "system",
- [LOG_ID_CRASH] = "crash"
+ [LOG_ID_CRASH] = "crash",
+ [LOG_ID_KERNEL] = "kernel",
};
const char *android_log_id_to_name(log_id_t log_id)
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index a865093..bdee28f 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -90,15 +90,6 @@
return (g_log_status == kLogAvailable);
}
-#if !FAKE_LOG_DEVICE
-/* give up, resources too limited */
-static int __write_to_log_null(log_id_t log_fd __unused, struct iovec *vec __unused,
- size_t nr __unused)
-{
- return -1;
-}
-#endif
-
/* log_init_lock assumed */
static int __write_to_log_initialize()
{
@@ -111,40 +102,32 @@
log_fds[i] = fakeLogOpen(buf, O_WRONLY);
}
#else
- if (logd_fd >= 0) {
- i = logd_fd;
- logd_fd = -1;
- close(i);
+ if (pstore_fd < 0) {
+ pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
}
- if (pstore_fd >= 0) {
- i = pstore_fd;
- pstore_fd = -1;
- close(i);
- }
- pstore_fd = open("/dev/pmsg0", O_WRONLY);
- i = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (i < 0) {
- ret = -errno;
- write_to_log = __write_to_log_null;
- } else if (fcntl(i, F_SETFL, O_NONBLOCK) < 0) {
- ret = -errno;
- close(i);
- i = -1;
- write_to_log = __write_to_log_null;
- } else {
- struct sockaddr_un un;
- memset(&un, 0, sizeof(struct sockaddr_un));
- un.sun_family = AF_UNIX;
- strcpy(un.sun_path, "/dev/socket/logdw");
-
- if (connect(i, (struct sockaddr *)&un, sizeof(struct sockaddr_un)) < 0) {
+ if (logd_fd < 0) {
+ i = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0));
+ if (i < 0) {
+ ret = -errno;
+ } else if (TEMP_FAILURE_RETRY(fcntl(i, F_SETFL, O_NONBLOCK)) < 0) {
ret = -errno;
close(i);
- i = -1;
+ } else {
+ struct sockaddr_un un;
+ memset(&un, 0, sizeof(struct sockaddr_un));
+ un.sun_family = AF_UNIX;
+ strcpy(un.sun_path, "/dev/socket/logdw");
+
+ if (TEMP_FAILURE_RETRY(connect(i, (struct sockaddr *)&un,
+ sizeof(struct sockaddr_un))) < 0) {
+ ret = -errno;
+ close(i);
+ } else {
+ logd_fd = i;
+ }
}
}
- logd_fd = i;
#endif
return ret;
@@ -178,6 +161,10 @@
static pid_t last_pid = (pid_t) -1;
static atomic_int_fast32_t dropped;
+ if (!nr) {
+ return -EINVAL;
+ }
+
if (last_uid == AID_ROOT) { /* have we called to get the UID yet? */
last_uid = getuid();
}
@@ -289,6 +276,8 @@
#if !defined(_WIN32)
pthread_mutex_lock(&log_init_lock);
#endif
+ close(logd_fd);
+ logd_fd = -1;
ret = __write_to_log_initialize();
#if !defined(_WIN32)
pthread_mutex_unlock(&log_init_lock);
@@ -321,7 +310,8 @@
[LOG_ID_RADIO] = "radio",
[LOG_ID_EVENTS] = "events",
[LOG_ID_SYSTEM] = "system",
- [LOG_ID_CRASH] = "crash"
+ [LOG_ID_CRASH] = "crash",
+ [LOG_ID_KERNEL] = "kernel",
};
const char *android_log_id_to_name(log_id_t log_id)
@@ -347,6 +337,11 @@
#if !defined(_WIN32)
pthread_mutex_unlock(&log_init_lock);
#endif
+#if (FAKE_LOG_DEVICE == 0)
+ if (pstore_fd >= 0) {
+ __write_to_log_daemon(log_id, vec, nr);
+ }
+#endif
return ret;
}
@@ -362,43 +357,7 @@
int __android_log_write(int prio, const char *tag, const char *msg)
{
- struct iovec vec[3];
- log_id_t log_id = LOG_ID_MAIN;
- char tmp_tag[32];
-
- if (!tag)
- tag = "";
-
- /* XXX: This needs to go! */
- if (!strcmp(tag, "HTC_RIL") ||
- !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
- !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
- !strcmp(tag, "AT") ||
- !strcmp(tag, "GSM") ||
- !strcmp(tag, "STK") ||
- !strcmp(tag, "CDMA") ||
- !strcmp(tag, "PHONE") ||
- !strcmp(tag, "SMS")) {
- log_id = LOG_ID_RADIO;
- /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
- snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
- tag = tmp_tag;
- }
-
-#if __BIONIC__
- if (prio == ANDROID_LOG_FATAL) {
- android_set_abort_message(msg);
- }
-#endif
-
- vec[0].iov_base = (unsigned char *) &prio;
- vec[0].iov_len = 1;
- vec[1].iov_base = (void *) tag;
- vec[1].iov_len = strlen(tag) + 1;
- vec[2].iov_base = (void *) msg;
- vec[2].iov_len = strlen(msg) + 1;
-
- return write_to_log(log_id, vec, 3);
+ return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
}
int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
@@ -426,6 +385,12 @@
tag = tmp_tag;
}
+#if __BIONIC__
+ if (prio == ANDROID_LOG_FATAL) {
+ android_set_abort_message(msg);
+ }
+#endif
+
vec[0].iov_base = (unsigned char *) &prio;
vec[0].iov_len = 1;
vec[1].iov_base = (void *) tag;
diff --git a/liblog/logd_write_kern.c b/liblog/logd_write_kern.c
index ca63067..8742b34 100644
--- a/liblog/logd_write_kern.c
+++ b/liblog/logd_write_kern.c
@@ -139,41 +139,7 @@
int __android_log_write(int prio, const char *tag, const char *msg)
{
- struct iovec vec[3];
- log_id_t log_id = LOG_ID_MAIN;
- char tmp_tag[32];
-
- if (!tag)
- tag = "";
-
- /* XXX: This needs to go! */
- if (!strcmp(tag, "HTC_RIL") ||
- !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
- !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
- !strcmp(tag, "AT") ||
- !strcmp(tag, "GSM") ||
- !strcmp(tag, "STK") ||
- !strcmp(tag, "CDMA") ||
- !strcmp(tag, "PHONE") ||
- !strcmp(tag, "SMS")) {
- log_id = LOG_ID_RADIO;
- /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
- snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
- tag = tmp_tag;
- }
-
- if (prio == ANDROID_LOG_FATAL) {
- android_set_abort_message(msg);
- }
-
- vec[0].iov_base = (unsigned char *) &prio;
- vec[0].iov_len = 1;
- vec[1].iov_base = (void *) tag;
- vec[1].iov_len = strlen(tag) + 1;
- vec[2].iov_base = (void *) msg;
- vec[2].iov_len = strlen(msg) + 1;
-
- return write_to_log(log_id, vec, 3);
+ return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
}
int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
@@ -201,6 +167,10 @@
tag = tmp_tag;
}
+ if (prio == ANDROID_LOG_FATAL) {
+ android_set_abort_message(msg);
+ }
+
vec[0].iov_base = (unsigned char *) &prio;
vec[0].iov_len = 1;
vec[1].iov_base = (void *) tag;
diff --git a/liblog/logprint.c b/liblog/logprint.c
index 7ba4c8e..a3f1d7e 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -26,6 +26,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <inttypes.h>
#include <sys/param.h>
#include <log/logd.h>
@@ -42,6 +43,7 @@
FilterInfo *filters;
AndroidLogPrintFormat format;
bool colored_output;
+ bool usec_time_output;
};
/*
@@ -184,6 +186,7 @@
p_ret->global_pri = ANDROID_LOG_VERBOSE;
p_ret->format = FORMAT_BRIEF;
p_ret->colored_output = false;
+ p_ret->usec_time_output = false;
return p_ret;
}
@@ -206,13 +209,19 @@
-void android_log_setPrintFormat(AndroidLogFormat *p_format,
+int android_log_setPrintFormat(AndroidLogFormat *p_format,
AndroidLogPrintFormat format)
{
- if (format == FORMAT_COLOR)
+ if (format == FORMAT_MODIFIER_COLOR) {
p_format->colored_output = true;
- else
- p_format->format = format;
+ return 0;
+ }
+ if (format == FORMAT_MODIFIER_TIME_USEC) {
+ p_format->usec_time_output = true;
+ return 0;
+ }
+ p_format->format = format;
+ return 1;
}
/**
@@ -230,7 +239,8 @@
else if (strcmp(formatString, "time") == 0) format = FORMAT_TIME;
else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME;
else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG;
- else if (strcmp(formatString, "color") == 0) format = FORMAT_COLOR;
+ else if (strcmp(formatString, "color") == 0) format = FORMAT_MODIFIER_COLOR;
+ else if (strcmp(formatString, "usec") == 0) format = FORMAT_MODIFIER_TIME_USEC;
else format = FORMAT_OFF;
return format;
@@ -432,7 +442,7 @@
low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
- return ((long long) high << 32) | (long long) low;
+ return ((uint64_t) high << 32) | (uint64_t) low;
}
@@ -490,7 +500,7 @@
case EVENT_TYPE_LONG:
/* 64-bit signed long */
{
- long long lval;
+ uint64_t lval;
if (eventDataLen < 8)
return -1;
@@ -498,7 +508,30 @@
eventData += 8;
eventDataLen -= 8;
- outCount = snprintf(outBuf, outBufLen, "%lld", lval);
+ outCount = snprintf(outBuf, outBufLen, "%" PRId64, lval);
+ if (outCount < outBufLen) {
+ outBuf += outCount;
+ outBufLen -= outCount;
+ } else {
+ /* halt output */
+ goto no_room;
+ }
+ }
+ break;
+ case EVENT_TYPE_FLOAT:
+ /* float */
+ {
+ uint32_t ival;
+ float fval;
+
+ if (eventDataLen < 4)
+ return -1;
+ ival = get4LE(eventData);
+ fval = *(float*)&ival;
+ eventData += 4;
+ eventDataLen -= 4;
+
+ outCount = snprintf(outBuf, outBufLen, "%f", fval);
if (outCount < outBufLen) {
outBuf += outCount;
outBufLen -= outCount;
@@ -721,7 +754,7 @@
struct tm tmBuf;
#endif
struct tm* ptm;
- char timeBuf[32];
+ char timeBuf[32]; /* good margin, 23+nul for msec, 26+nul for usec */
char prefixBuf[128], suffixBuf[128];
char priChar;
int prefixSuffixIsHeaderFooter = 0;
@@ -747,6 +780,14 @@
#endif
//strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
+ len = strlen(timeBuf);
+ if (p_format->usec_time_output) {
+ snprintf(timeBuf + len, sizeof(timeBuf) - len,
+ ".%06ld", entry->tv_nsec / 1000);
+ } else {
+ snprintf(timeBuf + len, sizeof(timeBuf) - len,
+ ".%03ld", entry->tv_nsec / 1000000);
+ }
/*
* Construct a buffer containing the log header and log message.
@@ -787,23 +828,21 @@
break;
case FORMAT_TIME:
len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "%s.%03ld %c/%-8s(%5d): ", timeBuf, entry->tv_nsec / 1000000,
- priChar, entry->tag, entry->pid);
+ "%s %c/%-8s(%5d): ", timeBuf, priChar, entry->tag, entry->pid);
strcpy(suffixBuf + suffixLen, "\n");
++suffixLen;
break;
case FORMAT_THREADTIME:
len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000,
+ "%s %5d %5d %c %-8s: ", timeBuf,
entry->pid, entry->tid, priChar, entry->tag);
strcpy(suffixBuf + suffixLen, "\n");
++suffixLen;
break;
case FORMAT_LONG:
len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "[ %s.%03ld %5d:%5d %c/%-8s ]\n",
- timeBuf, entry->tv_nsec / 1000000, entry->pid,
- entry->tid, priChar, entry->tag);
+ "[ %s %5d:%5d %c/%-8s ]\n",
+ timeBuf, entry->pid, entry->tid, priChar, entry->tag);
strcpy(suffixBuf + suffixLen, "\n\n");
suffixLen += 2;
prefixSuffixIsHeaderFooter = 1;
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk
index 8137a75..d75bbc9 100644
--- a/liblog/tests/Android.mk
+++ b/liblog/tests/Android.mk
@@ -39,7 +39,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE := $(test_module_prefix)benchmarks
LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_CFLAGS += $(benchmark_c_flags)
LOCAL_SHARED_LIBRARIES += liblog libm
LOCAL_SRC_FILES := $(benchmark_src_files)
@@ -77,7 +76,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE := $(test_module_prefix)unit-tests
LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_CFLAGS += $(test_c_flags)
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_SRC_FILES := $(test_src_files)
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 979aded..b594634 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -266,3 +266,17 @@
android_logger_list_free(logger_list);
}
BENCHMARK(BM_log_delay);
+
+/*
+ * Measure the time it takes for __android_log_is_loggable.
+ */
+static void BM_is_loggable(int iters) {
+ StartBenchmarkTiming();
+
+ for (int i = 0; i < iters; ++i) {
+ __android_log_is_loggable(ANDROID_LOG_WARN, "logd", ANDROID_LOG_VERBOSE);
+ }
+
+ StopBenchmarkTiming();
+}
+BENCHMARK(BM_is_loggable);
diff --git a/libnetutils/Android.mk b/libnetutils/Android.mk
index 1f61511..2060df4 100644
--- a/libnetutils/Android.mk
+++ b/libnetutils/Android.mk
@@ -17,3 +17,10 @@
LOCAL_CFLAGS := -Werror
include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := dhcptool.c
+LOCAL_SHARED_LIBRARIES := libnetutils
+LOCAL_MODULE := dhcptool
+LOCAL_MODULE_TAGS := debug
+include $(BUILD_EXECUTABLE)
diff --git a/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c
index 0f7c384..70e37c6 100644
--- a/libnetutils/dhcp_utils.c
+++ b/libnetutils/dhcp_utils.c
@@ -72,14 +72,16 @@
maxnaps = 1;
}
- while (maxnaps-- > 0) {
- usleep(NAP_TIME * 1000);
+ while (maxnaps-- >= 0) {
if (property_get(name, value, NULL)) {
if (desired_value == NULL ||
strcmp(value, desired_value) == 0) {
return 0;
}
}
+ if (maxnaps >= 0) {
+ usleep(NAP_TIME * 1000);
+ }
}
return -1; /* failure */
}
diff --git a/libnetutils/dhcptool.c b/libnetutils/dhcptool.c
new file mode 100644
index 0000000..352ac5e
--- /dev/null
+++ b/libnetutils/dhcptool.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <error.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <netutils/dhcp.h>
+#include <netutils/ifc.h>
+
+int main(int argc, char* argv[]) {
+ if (argc != 2) {
+ error(EXIT_FAILURE, 0, "usage: %s INTERFACE", argv[0]);
+ }
+
+ char* interface = argv[1];
+ if (ifc_init()) {
+ error(EXIT_FAILURE, errno, "dhcptool %s: ifc_init failed", interface);
+ }
+
+ int rc = do_dhcp(interface);
+ if (rc) {
+ error(0, errno, "dhcptool %s: do_dhcp failed", interface);
+ }
+
+ ifc_close();
+
+ return rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
index aa614bc..697db25 100644
--- a/libpixelflinger/Android.mk
+++ b/libpixelflinger/Android.mk
@@ -62,6 +62,8 @@
LOCAL_SRC_FILES_arm64 := $(PIXELFLINGER_SRC_FILES_arm64)
LOCAL_SRC_FILES_mips := $(PIXELFLINGER_SRC_FILES_mips)
LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS)
LOCAL_SHARED_LIBRARIES := libcutils liblog
# Really this should go away entirely or at least not depend on
diff --git a/include/pixelflinger/format.h b/libpixelflinger/include/pixelflinger/format.h
similarity index 100%
rename from include/pixelflinger/format.h
rename to libpixelflinger/include/pixelflinger/format.h
diff --git a/include/pixelflinger/pixelflinger.h b/libpixelflinger/include/pixelflinger/pixelflinger.h
similarity index 100%
rename from include/pixelflinger/pixelflinger.h
rename to libpixelflinger/include/pixelflinger/pixelflinger.h
diff --git a/include/private/pixelflinger/ggl_context.h b/libpixelflinger/include/private/pixelflinger/ggl_context.h
similarity index 100%
rename from include/private/pixelflinger/ggl_context.h
rename to libpixelflinger/include/private/pixelflinger/ggl_context.h
diff --git a/include/private/pixelflinger/ggl_fixed.h b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
similarity index 100%
rename from include/private/pixelflinger/ggl_fixed.h
rename to libpixelflinger/include/private/pixelflinger/ggl_fixed.h
diff --git a/libpixelflinger/tests/arch-arm64/assembler/Android.mk b/libpixelflinger/tests/arch-arm64/assembler/Android.mk
index 961f323..448d298 100644
--- a/libpixelflinger/tests/arch-arm64/assembler/Android.mk
+++ b/libpixelflinger/tests/arch-arm64/assembler/Android.mk
@@ -13,7 +13,7 @@
libpixelflinger
LOCAL_C_INCLUDES := \
- system/core/libpixelflinger
+ $(LOCAL_PATH)/../../..
LOCAL_MODULE:= test-pixelflinger-arm64-assembler-test
diff --git a/libpixelflinger/tests/arch-arm64/disassembler/Android.mk b/libpixelflinger/tests/arch-arm64/disassembler/Android.mk
index 8f62f09..d8f7e69 100644
--- a/libpixelflinger/tests/arch-arm64/disassembler/Android.mk
+++ b/libpixelflinger/tests/arch-arm64/disassembler/Android.mk
@@ -7,9 +7,6 @@
LOCAL_SHARED_LIBRARIES :=
-LOCAL_C_INCLUDES := \
- system/core/libpixelflinger/codeflinger
-
LOCAL_MODULE:= test-pixelflinger-arm64-disassembler-test
LOCAL_MODULE_TAGS := tests
diff --git a/libpixelflinger/tests/codegen/Android.mk b/libpixelflinger/tests/codegen/Android.mk
index bc07015..2f9ca2f 100644
--- a/libpixelflinger/tests/codegen/Android.mk
+++ b/libpixelflinger/tests/codegen/Android.mk
@@ -9,7 +9,7 @@
libpixelflinger
LOCAL_C_INCLUDES := \
- system/core/libpixelflinger
+ $(LOCAL_PATH)/../..
LOCAL_MODULE:= test-opengl-codegen
diff --git a/libpixelflinger/tests/gglmul/Android.mk b/libpixelflinger/tests/gglmul/Android.mk
index f479fa1..75bd39e 100644
--- a/libpixelflinger/tests/gglmul/Android.mk
+++ b/libpixelflinger/tests/gglmul/Android.mk
@@ -7,7 +7,7 @@
LOCAL_SHARED_LIBRARIES :=
LOCAL_C_INCLUDES := \
- system/core/libpixelflinger
+ $(LOCAL_PATH)/../../include
LOCAL_MODULE:= test-pixelflinger-gglmul
diff --git a/libprocessgroup/Android.mk b/libprocessgroup/Android.mk
index 051999a..ee6ba58 100644
--- a/libprocessgroup/Android.mk
+++ b/libprocessgroup/Android.mk
@@ -8,7 +8,6 @@
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_CFLAGS := -Wall -Werror
LOCAL_REQUIRED_MODULE := processgroup_cleanup
-include external/libcxx/libcxx.mk
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
diff --git a/libsparse/append2simg.c b/libsparse/append2simg.c
index 65e6cc2..1cf827c 100644
--- a/libsparse/append2simg.c
+++ b/libsparse/append2simg.c
@@ -82,7 +82,7 @@
exit(-1);
}
- sparse_output = sparse_file_import_auto(output, true);
+ sparse_output = sparse_file_import_auto(output, true, true);
if (!sparse_output) {
fprintf(stderr, "Couldn't import output file\n");
exit(-1);
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index 8b757d2..42d4adb 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -234,6 +234,7 @@
*
* @fd - file descriptor to read from
* @crc - verify the crc of a file in the Android sparse file format
+ * @verbose - whether to use verbose logging
*
* Reads an existing sparse or normal file into a sparse file cookie.
* Attempts to determine if the file is sparse or not by looking for the sparse
@@ -243,7 +244,7 @@
*
* Returns a new sparse file cookie on success, NULL on error.
*/
-struct sparse_file *sparse_file_import_auto(int fd, bool crc);
+struct sparse_file *sparse_file_import_auto(int fd, bool crc, bool verbose);
/** sparse_file_resparse - rechunk an existing sparse file into smaller files
*
diff --git a/libsparse/simg_dump.py b/libsparse/simg_dump.py
index 6ece31d..c70d45f 100755
--- a/libsparse/simg_dump.py
+++ b/libsparse/simg_dump.py
@@ -135,7 +135,7 @@
break;
else:
crc_bin = FH.read(4)
- crc = struct.unpack("<I", crc)
+ crc = struct.unpack("<I", crc_bin)
print("Unverified CRC32 0x%08X" % (crc))
else:
print("Unknown chunk type 0x%04X" % (chunk_type), end="")
diff --git a/libsparse/sparse.c b/libsparse/sparse.c
index baa30cd..311678a 100644
--- a/libsparse/sparse.c
+++ b/libsparse/sparse.c
@@ -101,26 +101,32 @@
return chunks;
}
-static void sparse_file_write_block(struct output_file *out,
+static int sparse_file_write_block(struct output_file *out,
struct backed_block *bb)
{
+ int ret = -EINVAL;
+
switch (backed_block_type(bb)) {
case BACKED_BLOCK_DATA:
- write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));
+ ret = write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));
break;
case BACKED_BLOCK_FILE:
- write_file_chunk(out, backed_block_len(bb),
- backed_block_filename(bb), backed_block_file_offset(bb));
+ ret = write_file_chunk(out, backed_block_len(bb),
+ backed_block_filename(bb),
+ backed_block_file_offset(bb));
break;
case BACKED_BLOCK_FD:
- write_fd_chunk(out, backed_block_len(bb),
- backed_block_fd(bb), backed_block_file_offset(bb));
+ ret = write_fd_chunk(out, backed_block_len(bb),
+ backed_block_fd(bb),
+ backed_block_file_offset(bb));
break;
case BACKED_BLOCK_FILL:
- write_fill_chunk(out, backed_block_len(bb),
- backed_block_fill_val(bb));
+ ret = write_fill_chunk(out, backed_block_len(bb),
+ backed_block_fill_val(bb));
break;
}
+
+ return ret;
}
static int write_all_blocks(struct sparse_file *s, struct output_file *out)
@@ -128,6 +134,7 @@
struct backed_block *bb;
unsigned int last_block = 0;
int64_t pad;
+ int ret = 0;
for (bb = backed_block_iter_new(s->backed_block_list); bb;
bb = backed_block_iter_next(bb)) {
@@ -135,7 +142,9 @@
unsigned int blocks = backed_block_block(bb) - last_block;
write_skip_chunk(out, (int64_t)blocks * s->block_size);
}
- sparse_file_write_block(out, bb);
+ ret = sparse_file_write_block(out, bb);
+ if (ret)
+ return ret;
last_block = backed_block_block(bb) +
DIV_ROUND_UP(backed_block_len(bb), s->block_size);
}
@@ -229,13 +238,15 @@
struct backed_block *last_bb = NULL;
struct backed_block *bb;
struct backed_block *start;
+ unsigned int last_block = 0;
int64_t file_len = 0;
+ int ret;
/*
- * overhead is sparse file header, initial skip chunk, split chunk, end
- * skip chunk, and crc chunk.
+ * overhead is sparse file header, the potential end skip
+ * chunk and crc chunk.
*/
- int overhead = sizeof(sparse_header_t) + 4 * sizeof(chunk_header_t) +
+ int overhead = sizeof(sparse_header_t) + 2 * sizeof(chunk_header_t) +
sizeof(uint32_t);
len -= overhead;
@@ -248,28 +259,39 @@
for (bb = start; bb; bb = backed_block_iter_next(bb)) {
count = 0;
+ if (backed_block_block(bb) > last_block)
+ count += sizeof(chunk_header_t);
+ last_block = backed_block_block(bb) +
+ DIV_ROUND_UP(backed_block_len(bb), to->block_size);
+
/* will call out_counter_write to update count */
- sparse_file_write_block(out_counter, bb);
+ ret = sparse_file_write_block(out_counter, bb);
+ if (ret) {
+ bb = NULL;
+ goto out;
+ }
if (file_len + count > len) {
/*
* If the remaining available size is more than 1/8th of the
* requested size, split the chunk. Results in sparse files that
* are at least 7/8ths of the requested size
*/
+ file_len += sizeof(chunk_header_t);
if (!last_bb || (len - file_len > (len / 8))) {
backed_block_split(from->backed_block_list, bb, len - file_len);
last_bb = bb;
}
- goto out;
+ goto move;
}
file_len += count;
last_bb = bb;
}
-out:
+move:
backed_block_list_move(from->backed_block_list,
to->backed_block_list, start, last_bb);
+out:
output_file_close(out_counter);
return bb;
diff --git a/libsparse/sparse_read.c b/libsparse/sparse_read.c
index 8e188e9..9b10293 100644
--- a/libsparse/sparse_read.c
+++ b/libsparse/sparse_read.c
@@ -472,13 +472,13 @@
return s;
}
-struct sparse_file *sparse_file_import_auto(int fd, bool crc)
+struct sparse_file *sparse_file_import_auto(int fd, bool crc, bool verbose)
{
struct sparse_file *s;
int64_t len;
int ret;
- s = sparse_file_import(fd, true, crc);
+ s = sparse_file_import(fd, verbose, crc);
if (s) {
return s;
}
diff --git a/libsync/tests/Android.mk b/libsync/tests/Android.mk
index ad20e50..8137c7a 100644
--- a/libsync/tests/Android.mk
+++ b/libsync/tests/Android.mk
@@ -17,10 +17,8 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-include external/libcxx/libcxx.mk
LOCAL_CLANG := true
LOCAL_MODULE := sync-unit-tests
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers -Wno-sign-compare
LOCAL_SHARED_LIBRARIES += libsync
LOCAL_STATIC_LIBRARIES += libgtest_main
diff --git a/libutils/tests/Android.mk b/libutils/tests/Android.mk
index 634f44f..7cfad89 100644
--- a/libutils/tests/Android.mk
+++ b/libutils/tests/Android.mk
@@ -18,7 +18,6 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_MODULE := libutils_tests
diff --git a/libziparchive/Android.mk b/libziparchive/Android.mk
index 3937449..a3087ee 100644
--- a/libziparchive/Android.mk
+++ b/libziparchive/Android.mk
@@ -18,21 +18,19 @@
source_files := zip_archive.cc
include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_CPP_EXTENSION := .cc
LOCAL_SRC_FILES := ${source_files}
LOCAL_STATIC_LIBRARIES := libz
-LOCAL_SHARED_LIBRARIES := libutils
+LOCAL_SHARED_LIBRARIES := libutils libbase
LOCAL_MODULE:= libziparchive
LOCAL_CFLAGS := -Werror -Wall
LOCAL_CPPFLAGS := -Wold-style-cast
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_CPP_EXTENSION := .cc
LOCAL_SRC_FILES := ${source_files}
-LOCAL_STATIC_LIBRARIES := libz libutils
+LOCAL_STATIC_LIBRARIES := libz libutils libbase
LOCAL_MODULE:= libziparchive-host
LOCAL_CFLAGS := -Werror
ifneq ($(strip $(USE_MINGW)),)
@@ -42,11 +40,10 @@
include $(BUILD_HOST_STATIC_LIBRARY)
include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_CPP_EXTENSION := .cc
LOCAL_SRC_FILES := ${source_files}
LOCAL_STATIC_LIBRARIES := libz libutils
-LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_SHARED_LIBRARIES := liblog libbase
LOCAL_MODULE:= libziparchive-host
LOCAL_CFLAGS := -Werror
LOCAL_MULTILIB := both
@@ -54,24 +51,22 @@
# Tests.
include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_MODULE := ziparchive-tests
LOCAL_CPP_EXTENSION := .cc
LOCAL_CFLAGS := -Werror
LOCAL_SRC_FILES := zip_archive_test.cc entry_name_utils_test.cc
-LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_SHARED_LIBRARIES := liblog libbase
LOCAL_STATIC_LIBRARIES := libziparchive libz libutils
include $(BUILD_NATIVE_TEST)
include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_MODULE := ziparchive-tests-host
LOCAL_CPP_EXTENSION := .cc
LOCAL_CFLAGS += \
-Werror \
-Wno-unnamed-type-template-args
LOCAL_SRC_FILES := zip_archive_test.cc entry_name_utils_test.cc
-LOCAL_SHARED_LIBRARIES := libziparchive-host liblog
+LOCAL_SHARED_LIBRARIES := libziparchive-host liblog libbase
LOCAL_STATIC_LIBRARIES := \
libz \
libutils
diff --git a/libziparchive/testdata/declaredlength.zip b/libziparchive/testdata/declaredlength.zip
new file mode 100644
index 0000000..773380c
--- /dev/null
+++ b/libziparchive/testdata/declaredlength.zip
Binary files differ
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index e820f2a..79c4c53 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -18,27 +18,30 @@
* Read-only access to Zip archives, with minimal heap allocation.
*/
-#include <memory>
-#include <vector>
-
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
-#include <log/log.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include <utils/Compat.h>
-#include <utils/FileMap.h>
-#include <zlib.h>
-#include <JNIHelp.h> // TEMP_FAILURE_RETRY may or may not be in unistd
+#include <memory>
+#include <vector>
+
+#include "base/file.h"
+#include "base/macros.h" // TEMP_FAILURE_RETRY may or may not be in unistd
+#include "base/memory.h"
+#include "log/log.h"
+#include "utils/Compat.h"
+#include "utils/FileMap.h"
+#include "zlib.h"
#include "entry_name_utils-inl.h"
#include "ziparchive/zip_archive.h"
+using android::base::get_unaligned;
// This is for windows. If we don't open a file in binary mode, weird
// things will happen.
@@ -46,11 +49,6 @@
#define O_BINARY 0
#endif
-#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
- TypeName(); \
- TypeName(const TypeName&); \
- void operator=(const TypeName&)
-
// The "end of central directory" (EOCD) record. Each archive
// contains exactly once such record which appears at the end of
// the archive. It contains archive wide information like the
@@ -88,7 +86,8 @@
// Length of the central directory comment.
uint16_t comment_length;
private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(EocdRecord);
+ EocdRecord() = default;
+ DISALLOW_COPY_AND_ASSIGN(EocdRecord);
} __attribute__((packed));
// A structure representing the fixed length fields for a single
@@ -141,7 +140,8 @@
// beginning of this archive.
uint32_t local_file_header_offset;
private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(CentralDirectoryRecord);
+ CentralDirectoryRecord() = default;
+ DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
} __attribute__((packed));
// The local file header for a given entry. This duplicates information
@@ -178,7 +178,8 @@
// will appear immediately after the entry file name.
uint16_t extra_field_length;
private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(LocalFileHeader);
+ LocalFileHeader() = default;
+ DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
} __attribute__((packed));
struct DataDescriptor {
@@ -192,10 +193,10 @@
// Uncompressed size of the entry.
uint32_t uncompressed_size;
private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(DataDescriptor);
+ DataDescriptor() = default;
+ DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
} __attribute__((packed));
-#undef DISALLOW_IMPLICIT_CONSTRUCTORS
static const uint32_t kGPBDDFlagMask = 0x0008; // mask value that signifies that the entry has a DD
@@ -268,8 +269,6 @@
static const int32_t kErrorMessageLowerBound = -13;
-static const char kTempMappingFileName[] = "zip: ExtractFileToFile";
-
/*
* A Read-only Zip archive.
*
@@ -327,35 +326,6 @@
}
};
-static int32_t CopyFileToFile(int fd, uint8_t* begin, const uint32_t length, uint64_t *crc_out) {
- static const uint32_t kBufSize = 32768;
- uint8_t buf[kBufSize];
-
- uint32_t count = 0;
- uint64_t crc = 0;
- while (count < length) {
- uint32_t remaining = length - count;
-
- // Safe conversion because kBufSize is narrow enough for a 32 bit signed
- // value.
- ssize_t get_size = (remaining > kBufSize) ? kBufSize : remaining;
- ssize_t actual = TEMP_FAILURE_RETRY(read(fd, buf, get_size));
-
- if (actual != get_size) {
- ALOGW("CopyFileToFile: copy read failed (" ZD " vs " ZD ")", actual, get_size);
- return kIoError;
- }
-
- memcpy(begin + count, buf, get_size);
- crc = crc32(crc, buf, get_size);
- count += get_size;
- }
-
- *crc_out = crc;
-
- return 0;
-}
-
/*
* Round up to the next highest power of 2.
*
@@ -462,10 +432,12 @@
*/
int i = read_amount - sizeof(EocdRecord);
for (; i >= 0; i--) {
- if (scan_buffer[i] == 0x50 &&
- ((*reinterpret_cast<uint32_t*>(&scan_buffer[i])) == EocdRecord::kSignature)) {
- ALOGV("+++ Found EOCD at buf+%d", i);
- break;
+ if (scan_buffer[i] == 0x50) {
+ uint32_t* sig_addr = reinterpret_cast<uint32_t*>(&scan_buffer[i]);
+ if (get_unaligned<uint32_t>(sig_addr) == EocdRecord::kSignature) {
+ ALOGV("+++ Found EOCD at buf+%d", i);
+ break;
+ }
}
}
if (i < 0) {
@@ -824,7 +796,7 @@
// name in the central directory.
if (lfh->file_name_length == nameLen) {
const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader);
- if (name_offset + lfh->file_name_length >= cd_offset) {
+ if (name_offset + lfh->file_name_length > cd_offset) {
ALOGW("Zip: Invalid declared length");
return kInvalidOffset;
}
@@ -973,6 +945,117 @@
return kIterationEnd;
}
+class Writer {
+ public:
+ virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
+ virtual ~Writer() {}
+ protected:
+ Writer() = default;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Writer);
+};
+
+// A Writer that writes data to a fixed size memory region.
+// The size of the memory region must be equal to the total size of
+// the data appended to it.
+class MemoryWriter : public Writer {
+ public:
+ MemoryWriter(uint8_t* buf, size_t size) : Writer(),
+ buf_(buf), size_(size), bytes_written_(0) {
+ }
+
+ virtual bool Append(uint8_t* buf, size_t buf_size) override {
+ if (bytes_written_ + buf_size > size_) {
+ ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
+ size_, bytes_written_ + buf_size);
+ return false;
+ }
+
+ memcpy(buf_ + bytes_written_, buf, buf_size);
+ bytes_written_ += buf_size;
+ return true;
+ }
+
+ private:
+ uint8_t* const buf_;
+ const size_t size_;
+ size_t bytes_written_;
+};
+
+// A Writer that appends data to a file |fd| at its current position.
+// The file will be truncated to the end of the written data.
+class FileWriter : public Writer {
+ public:
+
+ // Creates a FileWriter for |fd| and prepare to write |entry| to it,
+ // guaranteeing that the file descriptor is valid and that there's enough
+ // space on the volume to write out the entry completely and that the file
+ // is truncated to the correct length.
+ //
+ // Returns a valid FileWriter on success, |nullptr| if an error occurred.
+ static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry* entry) {
+ const uint32_t declared_length = entry->uncompressed_length;
+ const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
+ if (current_offset == -1) {
+ ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno));
+ return nullptr;
+ }
+
+ int result = 0;
+#if defined(__linux__)
+ if (declared_length > 0) {
+ // Make sure we have enough space on the volume to extract the compressed
+ // entry. Note that the call to ftruncate below will change the file size but
+ // will not allocate space on disk and this call to fallocate will not
+ // change the file size.
+ result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
+ if (result == -1) {
+ ALOGW("Zip: unable to allocate space for file to %" PRId64 ": %s",
+ static_cast<int64_t>(declared_length + current_offset), strerror(errno));
+ return std::unique_ptr<FileWriter>(nullptr);
+ }
+ }
+#endif // __linux__
+
+ result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
+ if (result == -1) {
+ ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
+ static_cast<int64_t>(declared_length + current_offset), strerror(errno));
+ return std::unique_ptr<FileWriter>(nullptr);
+ }
+
+ return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
+ }
+
+ virtual bool Append(uint8_t* buf, size_t buf_size) override {
+ if (total_bytes_written_ + buf_size > declared_length_) {
+ ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
+ declared_length_, total_bytes_written_ + buf_size);
+ return false;
+ }
+
+ const bool result = android::base::WriteFully(fd_, buf, buf_size);
+ if (result) {
+ total_bytes_written_ += buf_size;
+ } else {
+ ALOGW("Zip: unable to write " ZD " bytes to file; %s", buf_size, strerror(errno));
+ }
+
+ return result;
+ }
+ private:
+ FileWriter(const int fd, const size_t declared_length) :
+ Writer(),
+ fd_(fd),
+ declared_length_(declared_length),
+ total_bytes_written_(0) {
+ }
+
+ const int fd_;
+ const size_t declared_length_;
+ size_t total_bytes_written_;
+};
+
// This method is using libz macros with old-style-casts
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
@@ -981,9 +1064,8 @@
}
#pragma GCC diagnostic pop
-static int32_t InflateToFile(int fd, const ZipEntry* entry,
- uint8_t* begin, uint32_t length,
- uint64_t* crc_out) {
+static int32_t InflateEntryToWriter(int fd, const ZipEntry* entry,
+ Writer* writer, uint64_t* crc_out) {
const size_t kBufSize = 32768;
std::vector<uint8_t> read_buf(kBufSize);
std::vector<uint8_t> write_buf(kBufSize);
@@ -1028,7 +1110,6 @@
const uint32_t uncompressed_length = entry->uncompressed_length;
uint32_t compressed_length = entry->compressed_length;
- uint32_t write_count = 0;
do {
/* read as much as we can */
if (zstream.avail_in == 0) {
@@ -1058,12 +1139,10 @@
if (zstream.avail_out == 0 ||
(zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
const size_t write_size = zstream.next_out - &write_buf[0];
- // The file might have declared a bogus length.
- if (write_size + write_count > length) {
- return -1;
+ if (!writer->Append(&write_buf[0], write_size)) {
+ // The file might have declared a bogus length.
+ return kInconsistentInformation;
}
- memcpy(begin + write_count, &write_buf[0], write_size);
- write_count += write_size;
zstream.next_out = &write_buf[0];
zstream.avail_out = kBufSize;
@@ -1084,8 +1163,41 @@
return 0;
}
-int32_t ExtractToMemory(ZipArchiveHandle handle,
- ZipEntry* entry, uint8_t* begin, uint32_t size) {
+static int32_t CopyEntryToWriter(int fd, const ZipEntry* entry, Writer* writer,
+ uint64_t *crc_out) {
+ static const uint32_t kBufSize = 32768;
+ std::vector<uint8_t> buf(kBufSize);
+
+ const uint32_t length = entry->uncompressed_length;
+ uint32_t count = 0;
+ uint64_t crc = 0;
+ while (count < length) {
+ uint32_t remaining = length - count;
+
+ // Safe conversion because kBufSize is narrow enough for a 32 bit signed
+ // value.
+ const ssize_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
+ const ssize_t actual = TEMP_FAILURE_RETRY(read(fd, &buf[0], block_size));
+
+ if (actual != block_size) {
+ ALOGW("CopyFileToFile: copy read failed (" ZD " vs " ZD ")", actual, block_size);
+ return kIoError;
+ }
+
+ if (!writer->Append(&buf[0], block_size)) {
+ return kIoError;
+ }
+ crc = crc32(crc, &buf[0], block_size);
+ count += block_size;
+ }
+
+ *crc_out = crc;
+
+ return 0;
+}
+
+int32_t ExtractToWriter(ZipArchiveHandle handle,
+ ZipEntry* entry, Writer* writer) {
ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
const uint16_t method = entry->method;
off64_t data_offset = entry->offset;
@@ -1099,9 +1211,9 @@
int32_t return_value = -1;
uint64_t crc = 0;
if (method == kCompressStored) {
- return_value = CopyFileToFile(archive->fd, begin, size, &crc);
+ return_value = CopyEntryToWriter(archive->fd, entry, writer, &crc);
} else if (method == kCompressDeflated) {
- return_value = InflateToFile(archive->fd, entry, begin, size, &crc);
+ return_value = InflateEntryToWriter(archive->fd, entry, writer, &crc);
}
if (!return_value && entry->has_data_descriptor) {
@@ -1121,40 +1233,20 @@
return return_value;
}
+int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
+ uint8_t* begin, uint32_t size) {
+ std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
+ return ExtractToWriter(handle, entry, writer.get());
+}
+
int32_t ExtractEntryToFile(ZipArchiveHandle handle,
ZipEntry* entry, int fd) {
- const int32_t declared_length = entry->uncompressed_length;
-
- const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
- if (current_offset == -1) {
- ALOGW("Zip: unable to seek to current location on fd %d: %s", fd,
- strerror(errno));
+ std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
+ if (writer.get() == nullptr) {
return kIoError;
}
- int result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
- if (result == -1) {
- ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
- static_cast<int64_t>(declared_length + current_offset), strerror(errno));
- return kIoError;
- }
-
- // Don't attempt to map a region of length 0. We still need the
- // ftruncate() though, since the API guarantees that we will truncate
- // the file to the end of the uncompressed output.
- if (declared_length == 0) {
- return 0;
- }
-
- android::FileMap map;
- if (!map.create(kTempMappingFileName, fd, current_offset, declared_length, false)) {
- return kMmapFailed;
- }
-
- const int32_t error = ExtractToMemory(handle, entry,
- reinterpret_cast<uint8_t*>(map.getDataPtr()),
- map.getDataLength());
- return error;
+ return ExtractToWriter(handle, entry, writer.get());
}
const char* ErrorCodeString(int32_t error_code) {
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index c8dafa9..f8952ce 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -23,6 +23,7 @@
#include <unistd.h>
#include <vector>
+#include <base/file.h>
#include <gtest/gtest.h>
static std::string test_data_dir;
@@ -171,6 +172,22 @@
CloseArchive(handle);
}
+TEST(ziparchive, TestInvalidDeclaredLength) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
+
+ void* iteration_cookie;
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL));
+
+ ZipEntryName name;
+ ZipEntry data;
+
+ ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
+ ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
+
+ CloseArchive(handle);
+}
+
TEST(ziparchive, ExtractToMemory) {
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
@@ -212,6 +229,44 @@
0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904, 0x13880400,
0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000 };
+// This is a zip file containing a single entry (ab.txt) that contains
+// 90072 repetitions of the string "ab\n" and has an uncompressed length
+// of 270216 bytes.
+static const uint16_t kAbZip[] = {
+ 0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0,
+ 0x2cda, 0x011b, 0x0000, 0x1f88, 0x0004, 0x0006, 0x001c, 0x6261,
+ 0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
+ 0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000,
+ 0xc2ed, 0x0d31, 0x0000, 0x030c, 0x7fa0, 0x3b2e, 0x22ff, 0xa2aa,
+ 0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02,
+ 0x1403, 0x0000, 0x0800, 0xd200, 0x9851, 0xb046, 0xdac4, 0x1b2c,
+ 0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
+ 0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574,
+ 0x0554, 0x0300, 0x097c, 0x553a, 0x7875, 0x000b, 0x0401, 0x4289,
+ 0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
+ 0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000
+};
+
+static const uint8_t kAbTxtName[] = { 'a', 'b', '.', 't', 'x', 't' };
+static const uint16_t kAbTxtNameLength = sizeof(kAbTxtName);
+static const size_t kAbUncompressedSize = 270216;
+
static int make_temporary_file(const char* file_name_pattern) {
char full_path[1024];
// Account for differences between the host and the target.
@@ -259,6 +314,55 @@
close(output_fd);
}
+TEST(ziparchive, EntryLargerThan32K) {
+ char temp_file_pattern[] = "entry_larger_than_32k_test_XXXXXX";
+ int fd = make_temporary_file(temp_file_pattern);
+ ASSERT_NE(-1, fd);
+ ASSERT_TRUE(android::base::WriteFully(fd, reinterpret_cast<const uint8_t*>(kAbZip),
+ sizeof(kAbZip) - 1));
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd, "EntryLargerThan32KTest", &handle));
+
+ ZipEntry entry;
+ ZipEntryName ab_name;
+ ab_name.name = kAbTxtName;
+ ab_name.name_length = kAbTxtNameLength;
+ ASSERT_EQ(0, FindEntry(handle, ab_name, &entry));
+ ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
+
+ // Extract the entry to memory.
+ std::vector<uint8_t> buffer(kAbUncompressedSize);
+ ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], buffer.size()));
+
+ // Extract the entry to a file.
+ char output_file_pattern[] = "entry_larger_than_32k_test_output_XXXXXX";
+ int output_fd = make_temporary_file(output_file_pattern);
+ ASSERT_NE(-1, output_fd);
+ ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd));
+
+ // Make sure the extracted file size is as expected.
+ struct stat stat_buf;
+ ASSERT_EQ(0, fstat(output_fd, &stat_buf));
+ ASSERT_EQ(kAbUncompressedSize, static_cast<size_t>(stat_buf.st_size));
+
+ // Read the file back to a buffer and make sure the contents are
+ // the same as the memory buffer we extracted directly to.
+ std::vector<uint8_t> file_contents(kAbUncompressedSize);
+ ASSERT_EQ(0, lseek64(output_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFully(output_fd, &file_contents[0], file_contents.size()));
+ ASSERT_EQ(file_contents, buffer);
+
+ for (int i = 0; i < 90072; ++i) {
+ const uint8_t* line = &file_contents[0] + (3 * i);
+ ASSERT_EQ('a', line[0]);
+ ASSERT_EQ('b', line[1]);
+ ASSERT_EQ('\n', line[2]);
+ }
+
+ close(fd);
+ close(output_fd);
+}
+
TEST(ziparchive, TrailerAfterEOCD) {
char temp_file_pattern[] = "trailer_after_eocd_test_XXXXXX";
int fd = make_temporary_file(temp_file_pattern);
diff --git a/libzipfile/Android.mk b/libzipfile/Android.mk
deleted file mode 100644
index f054e15..0000000
--- a/libzipfile/Android.mk
+++ /dev/null
@@ -1,48 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# build host static library
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- centraldir.c \
- zipfile.c
-
-LOCAL_STATIC_LIBRARIES := libz
-
-LOCAL_MODULE:= libzipfile
-
-LOCAL_CFLAGS := -Werror
-
-LOCAL_MULTILIB := both
-
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-# build device static library
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- centraldir.c \
- zipfile.c
-
-LOCAL_STATIC_LIBRARIES := libz
-
-LOCAL_MODULE:= libzipfile
-
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_STATIC_LIBRARY)
-
-
-# build test_zipfile
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- test_zipfile.c
-
-LOCAL_STATIC_LIBRARIES := libzipfile libz
-
-LOCAL_MODULE := test_zipfile
-
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_HOST_EXECUTABLE)
diff --git a/libzipfile/MODULE_LICENSE_APACHE2 b/libzipfile/MODULE_LICENSE_APACHE2
deleted file mode 100644
index e69de29..0000000
--- a/libzipfile/MODULE_LICENSE_APACHE2
+++ /dev/null
diff --git a/libzipfile/NOTICE b/libzipfile/NOTICE
deleted file mode 100644
index c5b1efa..0000000
--- a/libzipfile/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
- Copyright (c) 2005-2008, The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
diff --git a/libzipfile/centraldir.c b/libzipfile/centraldir.c
deleted file mode 100644
index 69cf47a..0000000
--- a/libzipfile/centraldir.c
+++ /dev/null
@@ -1,222 +0,0 @@
-#include "private.h"
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include <utils/Compat.h>
-
-enum {
- // finding the directory
- CD_SIGNATURE = 0x06054b50,
- EOCD_LEN = 22, // EndOfCentralDir len, excl. comment
- MAX_COMMENT_LEN = 65535,
- MAX_EOCD_SEARCH = MAX_COMMENT_LEN + EOCD_LEN,
-
- // central directory entries
- ENTRY_SIGNATURE = 0x02014b50,
- ENTRY_LEN = 46, // CentralDirEnt len, excl. var fields
-
- // local file header
- LFH_SIZE = 30,
-};
-
-unsigned int
-read_le_int(const unsigned char* buf)
-{
- return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
-}
-
-unsigned int
-read_le_short(const unsigned char* buf)
-{
- return buf[0] | (buf[1] << 8);
-}
-
-static int
-read_central_dir_values(Zipfile* file, const unsigned char* buf, int len)
-{
- if (len < EOCD_LEN) {
- // looks like ZIP file got truncated
- fprintf(stderr, " Zip EOCD: expected >= %d bytes, found %d\n",
- EOCD_LEN, len);
- return -1;
- }
-
- file->disknum = read_le_short(&buf[0x04]);
- file->diskWithCentralDir = read_le_short(&buf[0x06]);
- file->entryCount = read_le_short(&buf[0x08]);
- file->totalEntryCount = read_le_short(&buf[0x0a]);
- file->centralDirSize = read_le_int(&buf[0x0c]);
- file->centralDirOffest = read_le_int(&buf[0x10]);
- file->commentLen = read_le_short(&buf[0x14]);
-
- if (file->commentLen > 0) {
- if (EOCD_LEN + file->commentLen > len) {
- fprintf(stderr, "EOCD(%d) + comment(%d) exceeds len (%d)\n",
- EOCD_LEN, file->commentLen, len);
- return -1;
- }
- file->comment = buf + EOCD_LEN;
- }
-
- return 0;
-}
-
-static int
-read_central_directory_entry(Zipfile* file, Zipentry* entry,
- const unsigned char** buf, ssize_t* len)
-{
- const unsigned char* p;
-
- unsigned short extraFieldLength;
- unsigned short fileCommentLength;
- unsigned long localHeaderRelOffset;
- unsigned int dataOffset;
-
- p = *buf;
-
- if (*len < ENTRY_LEN) {
- fprintf(stderr, "cde entry not large enough\n");
- return -1;
- }
-
- if (read_le_int(&p[0x00]) != ENTRY_SIGNATURE) {
- fprintf(stderr, "Whoops: didn't find expected signature\n");
- return -1;
- }
-
- entry->compressionMethod = read_le_short(&p[0x0a]);
- entry->compressedSize = read_le_int(&p[0x14]);
- entry->uncompressedSize = read_le_int(&p[0x18]);
- entry->fileNameLength = read_le_short(&p[0x1c]);
- extraFieldLength = read_le_short(&p[0x1e]);
- fileCommentLength = read_le_short(&p[0x20]);
- localHeaderRelOffset = read_le_int(&p[0x2a]);
-
- p += ENTRY_LEN;
-
- // filename
- if (entry->fileNameLength != 0) {
- entry->fileName = p;
- } else {
- entry->fileName = NULL;
- }
- p += entry->fileNameLength;
-
- // extra field
- p += extraFieldLength;
-
- // comment, if any
- p += fileCommentLength;
-
- *buf = p;
-
- // the size of the extraField in the central dir is how much data there is,
- // but the one in the local file header also contains some padding.
- p = file->buf + localHeaderRelOffset;
- extraFieldLength = read_le_short(&p[0x1c]);
-
- dataOffset = localHeaderRelOffset + LFH_SIZE
- + entry->fileNameLength + extraFieldLength;
- entry->data = file->buf + dataOffset;
-#if 0
- printf("file->buf=%p entry->data=%p dataOffset=%x localHeaderRelOffset=%d "
- "entry->fileNameLength=%d extraFieldLength=%d\n",
- file->buf, entry->data, dataOffset, localHeaderRelOffset,
- entry->fileNameLength, extraFieldLength);
-#endif
- return 0;
-}
-
-/*
- * Find the central directory and read the contents.
- *
- * The fun thing about ZIP archives is that they may or may not be
- * readable from start to end. In some cases, notably for archives
- * that were written to stdout, the only length information is in the
- * central directory at the end of the file.
- *
- * Of course, the central directory can be followed by a variable-length
- * comment field, so we have to scan through it backwards. The comment
- * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff
- * itself, plus apparently sometimes people throw random junk on the end
- * just for the fun of it.
- *
- * This is all a little wobbly. If the wrong value ends up in the EOCD
- * area, we're hosed. This appears to be the way that everbody handles
- * it though, so we're in pretty good company if this fails.
- */
-int
-read_central_dir(Zipfile *file)
-{
- int err;
-
- const unsigned char* buf = file->buf;
- ZD_TYPE bufsize = file->bufsize;
- const unsigned char* eocd;
- const unsigned char* p;
- const unsigned char* start;
- ssize_t len;
- int i;
-
- // too small to be a ZIP archive?
- if (bufsize < EOCD_LEN) {
- fprintf(stderr, "Length is " ZD " -- too small\n", bufsize);
- goto bail;
- }
-
- // find the end-of-central-dir magic
- if (bufsize > MAX_EOCD_SEARCH) {
- start = buf + bufsize - MAX_EOCD_SEARCH;
- } else {
- start = buf;
- }
- p = buf + bufsize - 4;
- while (p >= start) {
- if (*p == 0x50 && read_le_int(p) == CD_SIGNATURE) {
- eocd = p;
- break;
- }
- p--;
- }
- if (p < start) {
- fprintf(stderr, "EOCD not found, not Zip\n");
- goto bail;
- }
-
- // extract eocd values
- err = read_central_dir_values(file, eocd, (buf+bufsize)-eocd);
- if (err != 0) {
- goto bail;
- }
-
- if (file->disknum != 0
- || file->diskWithCentralDir != 0
- || file->entryCount != file->totalEntryCount) {
- fprintf(stderr, "Archive spanning not supported\n");
- goto bail;
- }
-
- // Loop through and read the central dir entries.
- p = buf + file->centralDirOffest;
- len = (buf+bufsize)-p;
- for (i=0; i < file->totalEntryCount; i++) {
- Zipentry* entry = malloc(sizeof(Zipentry));
- memset(entry, 0, sizeof(Zipentry));
-
- err = read_central_directory_entry(file, entry, &p, &len);
- if (err != 0) {
- fprintf(stderr, "read_central_directory_entry failed\n");
- free(entry);
- goto bail;
- }
-
- // add it to our list
- entry->next = file->entries;
- file->entries = entry;
- }
-
- return 0;
-bail:
- return -1;
-}
diff --git a/libzipfile/private.h b/libzipfile/private.h
deleted file mode 100644
index 06f788d..0000000
--- a/libzipfile/private.h
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef PRIVATE_H
-#define PRIVATE_H
-
-#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-typedef struct Zipentry {
- unsigned long fileNameLength;
- const unsigned char* fileName;
- unsigned short compressionMethod;
- unsigned int uncompressedSize;
- unsigned int compressedSize;
- const unsigned char* data;
-
- struct Zipentry* next;
-} Zipentry;
-
-typedef struct Zipfile
-{
- const unsigned char *buf;
- ssize_t bufsize;
-
- // Central directory
- unsigned short disknum; //mDiskNumber;
- unsigned short diskWithCentralDir; //mDiskWithCentralDir;
- unsigned short entryCount; //mNumEntries;
- unsigned short totalEntryCount; //mTotalNumEntries;
- unsigned int centralDirSize; //mCentralDirSize;
- unsigned int centralDirOffest; // offset from first disk //mCentralDirOffset;
- unsigned short commentLen; //mCommentLen;
- const unsigned char* comment; //mComment;
-
- Zipentry* entries;
-} Zipfile;
-
-int read_central_dir(Zipfile* file);
-
-unsigned int read_le_int(const unsigned char* buf);
-unsigned int read_le_short(const unsigned char* buf);
-
-#endif // PRIVATE_H
-
diff --git a/libzipfile/test_zipfile.c b/libzipfile/test_zipfile.c
deleted file mode 100644
index 1aaa913..0000000
--- a/libzipfile/test_zipfile.c
+++ /dev/null
@@ -1,94 +0,0 @@
-#include <zipfile/zipfile.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-void dump_zipfile(FILE* to, zipfile_t file);
-
-int
-main(int argc, char** argv)
-{
- FILE* f;
- size_t size, unsize;
- void* buf;
- void* scratch;
- zipfile_t zip;
- zipentry_t entry;
- int err;
- enum { HUH, LIST, UNZIP } what = HUH;
-
- if (strcmp(argv[2], "-l") == 0 && argc == 3) {
- what = LIST;
- }
- else if (strcmp(argv[2], "-u") == 0 && argc == 5) {
- what = UNZIP;
- }
- else {
- fprintf(stderr, "usage: test_zipfile ZIPFILE -l\n"
- " lists the files in the zipfile\n"
- " test_zipfile ZIPFILE -u FILENAME SAVETO\n"
- " saves FILENAME from the zip file into SAVETO\n");
- return 1;
- }
-
- f = fopen(argv[1], "r");
- if (f == NULL) {
- fprintf(stderr, "couldn't open %s\n", argv[1]);
- return 1;
- }
-
- fseek(f, 0, SEEK_END);
- size = ftell(f);
- rewind(f);
-
- buf = malloc(size);
- fread(buf, 1, size, f);
-
- zip = init_zipfile(buf, size);
- if (zip == NULL) {
- fprintf(stderr, "inti_zipfile failed\n");
- return 1;
- }
-
- fclose(f);
-
-
- switch (what)
- {
- case HUH:
- break;
- case LIST:
- dump_zipfile(stdout, zip);
- break;
- case UNZIP:
- entry = lookup_zipentry(zip, argv[3]);
- if (entry == NULL) {
- fprintf(stderr, "zip file '%s' does not contain file '%s'\n",
- argv[1], argv[1]);
- return 1;
- }
- f = fopen(argv[4], "w");
- if (f == NULL) {
- fprintf(stderr, "can't open file for writing '%s'\n", argv[4]);
- return 1;
- }
- unsize = get_zipentry_size(entry);
- size = unsize * 1.001;
- scratch = malloc(size);
- printf("scratch=%p\n", scratch);
- err = decompress_zipentry(entry, scratch, size);
- if (err != 0) {
- fprintf(stderr, "error decompressing file\n");
- return 1;
- }
- fwrite(scratch, unsize, 1, f);
- free(scratch);
- fclose(f);
- break;
- }
-
- free(buf);
-
- return 0;
-}
-
diff --git a/libzipfile/zipfile.c b/libzipfile/zipfile.c
deleted file mode 100644
index 1032ecc..0000000
--- a/libzipfile/zipfile.c
+++ /dev/null
@@ -1,159 +0,0 @@
-#include <zipfile/zipfile.h>
-
-#include "private.h"
-#include <stdlib.h>
-#include <string.h>
-#include <zlib.h>
-#define DEF_MEM_LEVEL 8 // normally in zutil.h?
-
-zipfile_t
-init_zipfile(const void* data, size_t size)
-{
- int err;
-
- Zipfile *file = malloc(sizeof(Zipfile));
- if (file == NULL) return NULL;
- memset(file, 0, sizeof(Zipfile));
- file->buf = data;
- file->bufsize = size;
-
- err = read_central_dir(file);
- if (err != 0) goto fail;
-
- return file;
-fail:
- free(file);
- return NULL;
-}
-
-void
-release_zipfile(zipfile_t f)
-{
- Zipfile* file = (Zipfile*)f;
- Zipentry* entry = file->entries;
- while (entry) {
- Zipentry* next = entry->next;
- free(entry);
- entry = next;
- }
- free(file);
-}
-
-zipentry_t
-lookup_zipentry(zipfile_t f, const char* entryName)
-{
- Zipfile* file = (Zipfile*)f;
- Zipentry* entry = file->entries;
- while (entry) {
- if (0 == memcmp(entryName, entry->fileName, entry->fileNameLength)) {
- return entry;
- }
- entry = entry->next;
- }
- return NULL;
-}
-
-size_t
-get_zipentry_size(zipentry_t entry)
-{
- return ((Zipentry*)entry)->uncompressedSize;
-}
-
-char*
-get_zipentry_name(zipentry_t entry)
-{
- Zipentry* e = (Zipentry*)entry;
- int l = e->fileNameLength;
- char* s = malloc(l+1);
- memcpy(s, e->fileName, l);
- s[l] = '\0';
- return s;
-}
-
-enum {
- STORED = 0,
- DEFLATED = 8
-};
-
-static int
-inflate_wrapper(unsigned char* out, int unlen, const unsigned char* in, int clen)
-{
- z_stream zstream;
- int err = 0;
- int zerr;
-
- memset(&zstream, 0, sizeof(zstream));
- zstream.zalloc = Z_NULL;
- zstream.zfree = Z_NULL;
- zstream.opaque = Z_NULL;
- zstream.next_in = (void*)in;
- zstream.avail_in = clen;
- zstream.next_out = (Bytef*) out;
- zstream.avail_out = unlen;
- zstream.data_type = Z_UNKNOWN;
-
- // Use the undocumented "negative window bits" feature to tell zlib
- // that there's no zlib header waiting for it.
- zerr = inflateInit2(&zstream, -MAX_WBITS);
- if (zerr != Z_OK) {
- return -1;
- }
-
- // uncompress the data
- zerr = inflate(&zstream, Z_FINISH);
- if (zerr != Z_STREAM_END) {
- fprintf(stderr, "zerr=%d Z_STREAM_END=%d total_out=%lu\n", zerr, Z_STREAM_END,
- zstream.total_out);
- err = -1;
- }
-
- inflateEnd(&zstream);
- return err;
-}
-
-int
-decompress_zipentry(zipentry_t e, void* buf, int bufsize)
-{
- Zipentry* entry = (Zipentry*)e;
- switch (entry->compressionMethod)
- {
- case STORED:
- memcpy(buf, entry->data, entry->uncompressedSize);
- return 0;
- case DEFLATED:
- return inflate_wrapper(buf, bufsize, entry->data, entry->compressedSize);
- default:
- return -1;
- }
-}
-
-void
-dump_zipfile(FILE* to, zipfile_t file)
-{
- Zipfile* zip = (Zipfile*)file;
- Zipentry* entry = zip->entries;
- int i;
-
- fprintf(to, "entryCount=%d\n", zip->entryCount);
- for (i=0; i<zip->entryCount; i++) {
- fprintf(to, " file \"");
- fwrite(entry->fileName, entry->fileNameLength, 1, to);
- fprintf(to, "\"\n");
- entry = entry->next;
- }
-}
-
-zipentry_t
-iterate_zipfile(zipfile_t file, void** cookie)
-{
- Zipentry* entry = (Zipentry*)*cookie;
- if (entry == NULL) {
- Zipfile* zip = (Zipfile*)file;
- *cookie = zip->entries;
- return *cookie;
- } else {
- entry = entry->next;
- *cookie = entry;
- return entry;
- }
-}
diff --git a/logcat/event.logtags b/logcat/event.logtags
index 1b5c6f4..909f8e2 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -21,6 +21,7 @@
# 2: long
# 3: string
# 4: list
+# 5: float
#
# The data unit is a number taken from the following list:
# 1: Number of objects
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index be96fc4..2c2d785 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -12,6 +12,7 @@
#include <signal.h>
#include <time.h>
#include <unistd.h>
+#include <sys/cdefs.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <arpa/inet.h>
@@ -24,7 +25,6 @@
#include <log/logprint.h>
#include <log/event_tag_map.h>
-#define DEFAULT_LOG_ROTATE_SIZE_KBYTES 16
#define DEFAULT_MAX_ROTATED_LOGS 4
static AndroidLogFormat * g_logformat;
@@ -46,6 +46,8 @@
binary = b;
next = NULL;
printed = false;
+ logger = NULL;
+ logger_list = NULL;
}
};
@@ -54,13 +56,17 @@
/* Global Variables */
static const char * g_outputFileName = NULL;
-static int g_logRotateSizeKBytes = 0; // 0 means "no log rotation"
-static int g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS; // 0 means "unbounded"
+// 0 means "no log rotation"
+static size_t g_logRotateSizeKBytes = 0;
+// 0 means "unbounded"
+static size_t g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS;
static int g_outFD = -1;
-static off_t g_outByteCount = 0;
+static size_t g_outByteCount = 0;
static int g_printBinary = 0;
static int g_devCount = 0; // >1 means multiple
+__noreturn static void logcat_panic(bool showHelp, const char *fmt, ...) __printflike(2,3);
+
static int openLogFile (const char *pathname)
{
return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
@@ -93,7 +99,12 @@
asprintf(&file0, "%s.%.*d", g_outputFileName, maxRotationCountDigits, i - 1);
}
- err = rename (file0, file1);
+ if (!file0 || !file1) {
+ perror("while rotating log files");
+ break;
+ }
+
+ err = rename(file0, file1);
if (err < 0 && errno != ENOENT) {
perror("while rotating log files");
@@ -103,11 +114,10 @@
free(file0);
}
- g_outFD = openLogFile (g_outputFileName);
+ g_outFD = openLogFile(g_outputFileName);
if (g_outFD < 0) {
- perror ("couldn't open output file");
- exit(-1);
+ logcat_panic(false, "couldn't open output file");
}
g_outByteCount = 0;
@@ -153,8 +163,7 @@
bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry);
if (bytesWritten < 0) {
- perror("output error");
- exit(-1);
+ logcat_panic(false, "output error");
}
}
@@ -179,8 +188,7 @@
dev->printed ? "switch to" : "beginning of",
dev->device);
if (write(g_outFD, buf, strlen(buf)) < 0) {
- perror("output error");
- exit(-1);
+ logcat_panic(false, "output error");
}
}
dev->printed = true;
@@ -199,11 +207,18 @@
g_outFD = openLogFile (g_outputFileName);
if (g_outFD < 0) {
- perror ("couldn't open output file");
- exit(-1);
+ logcat_panic(false, "couldn't open output file");
}
- fstat(g_outFD, &statbuf);
+ if (fstat(g_outFD, &statbuf) == -1) {
+ close(g_outFD);
+ logcat_panic(false, "couldn't get output file stat\n");
+ }
+
+ if ((size_t) statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
+ close(g_outFD);
+ logcat_panic(false, "invalid output file stat\n");
+ }
g_outByteCount = statbuf.st_size;
}
@@ -217,10 +232,10 @@
" -s Set default filter to silent.\n"
" Like specifying filterspec '*:S'\n"
" -f <filename> Log to file. Default to stdout\n"
- " -r [<kbytes>] Rotate log every kbytes. (16 if unspecified). Requires -f\n"
+ " -r <kbytes> Rotate log every kbytes. Requires -f\n"
" -n <count> Sets max number of rotated logs to <count>, default 4\n"
" -v <format> Sets the log print format, where <format> is:\n\n"
- " brief color long process raw tag thread threadtime time\n\n"
+ " brief color long process raw tag thread threadtime time usec\n\n"
" -D print dividers between each log buffer\n"
" -c clear (flush) the entire log and exit\n"
" -d dump the log and then exit (don't block)\n"
@@ -265,9 +280,6 @@
"or defaults to \"threadtime\"\n\n");
}
-
-} /* namespace android */
-
static int setLogFormat(const char * formatString)
{
static AndroidLogPrintFormat format;
@@ -279,9 +291,7 @@
return -1;
}
- android_log_setPrintFormat(g_logformat, format);
-
- return 0;
+ return android_log_setPrintFormat(g_logformat, format);
}
static const char multipliers[][2] = {
@@ -308,8 +318,46 @@
return multipliers[i];
}
+/*String to unsigned int, returns -1 if it fails*/
+static bool getSizeTArg(char *ptr, size_t *val, size_t min = 0,
+ size_t max = SIZE_MAX)
+{
+ char *endp;
+ errno = 0;
+ size_t ret = (size_t) strtoll(ptr, &endp, 0);
+
+ if (endp[0] != '\0' || errno != 0 ) {
+ return false;
+ }
+
+ if (ret > max || ret < min) {
+ return false;
+ }
+
+ *val = ret;
+ return true;
+}
+
+static void logcat_panic(bool showHelp, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+
+ if (showHelp) {
+ show_help(getprogname());
+ }
+
+ exit(EXIT_FAILURE);
+}
+
+} /* namespace android */
+
+
int main(int argc, char **argv)
{
+ using namespace android;
int err;
int hasSetLogFormat = 0;
int clearLog = 0;
@@ -324,7 +372,7 @@
log_device_t* dev;
bool printDividers = false;
struct logger_list *logger_list;
- unsigned int tail_lines = 0;
+ size_t tail_lines = 0;
log_time tail_time(log_time::EPOCH);
signal(SIGPIPE, exit);
@@ -332,14 +380,14 @@
g_logformat = android_log_format_new();
if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
- android::show_help(argv[0]);
- exit(0);
+ show_help(argv[0]);
+ return EXIT_SUCCESS;
}
for (;;) {
int ret;
- ret = getopt(argc, argv, "cdDLt:T:gG:sQf:r:n:v:b:BSpP:");
+ ret = getopt(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:");
if (ret < 0) {
break;
@@ -372,10 +420,9 @@
char *cp = tail_time.strptime(optarg,
log_time::default_format);
if (!cp) {
- fprintf(stderr,
- "ERROR: -%c \"%s\" not in \"%s\" time format\n",
- ret, optarg, log_time::default_format);
- exit(1);
+ logcat_panic(false,
+ "-%c \"%s\" not in \"%s\" time format\n",
+ ret, optarg, log_time::default_format);
}
if (*cp) {
char c = *cp;
@@ -386,8 +433,7 @@
*cp = c;
}
} else {
- tail_lines = atoi(optarg);
- if (!tail_lines) {
+ if (!getSizeTArg(optarg, &tail_lines, 1)) {
fprintf(stderr,
"WARNING: -%c %s invalid, setting to 1\n",
ret, optarg);
@@ -405,13 +451,11 @@
break;
case 'G': {
- // would use atol if not for the multiplier
- char *cp = optarg;
- setLogSize = 0;
- while (('0' <= *cp) && (*cp <= '9')) {
- setLogSize *= 10;
- setLogSize += *cp - '0';
- ++cp;
+ char *cp;
+ if (strtoll(optarg, &cp, 0) > 0) {
+ setLogSize = strtoll(optarg, &cp, 0);
+ } else {
+ setLogSize = 0;
}
switch(*cp) {
@@ -436,7 +480,7 @@
if (!setLogSize) {
fprintf(stderr, "ERROR: -G <num><multiplier>\n");
- exit(1);
+ return EXIT_FAILURE;
}
}
break;
@@ -458,7 +502,7 @@
}
devices = dev = NULL;
- android::g_devCount = 0;
+ g_devCount = 0;
for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
const char *name = android_log_id_to_name((log_id_t)i);
log_id_t log_id = android_name_to_log_id(name);
@@ -476,7 +520,7 @@
} else {
devices = dev = d;
}
- android::g_devCount++;
+ g_devCount++;
}
break;
}
@@ -492,56 +536,38 @@
} else {
devices = new log_device_t(optarg, binary);
}
- android::g_devCount++;
+ g_devCount++;
}
break;
case 'B':
- android::g_printBinary = 1;
+ g_printBinary = 1;
break;
case 'f':
// redirect output to a file
-
- android::g_outputFileName = optarg;
+ g_outputFileName = optarg;
break;
case 'r':
- if (optarg == NULL) {
- android::g_logRotateSizeKBytes
- = DEFAULT_LOG_ROTATE_SIZE_KBYTES;
- } else {
- if (!isdigit(optarg[0])) {
- fprintf(stderr,"Invalid parameter to -r\n");
- android::show_help(argv[0]);
- exit(-1);
- }
- android::g_logRotateSizeKBytes = atoi(optarg);
+ if (!getSizeTArg(optarg, &g_logRotateSizeKBytes, 1)) {
+ logcat_panic(true, "Invalid parameter %s to -r\n", optarg);
}
break;
case 'n':
- if (!isdigit(optarg[0])) {
- fprintf(stderr,"Invalid parameter to -r\n");
- android::show_help(argv[0]);
- exit(-1);
+ if (!getSizeTArg(optarg, &g_maxRotatedLogs, 1)) {
+ logcat_panic(true, "Invalid parameter %s to -n\n", optarg);
}
-
- android::g_maxRotatedLogs = atoi(optarg);
break;
case 'v':
err = setLogFormat (optarg);
if (err < 0) {
- fprintf(stderr,"Invalid parameter to -v\n");
- android::show_help(argv[0]);
- exit(-1);
+ logcat_panic(true, "Invalid parameter %s to -v\n", optarg);
}
-
- if (strcmp("color", optarg)) { // exception for modifiers
- hasSetLogFormat = 1;
- }
+ hasSetLogFormat |= err;
break;
case 'Q':
@@ -582,8 +608,9 @@
force_exit = 0;
}
/* if nothing found or invalid filters, exit quietly */
- if (force_exit)
- exit(0);
+ if (force_exit) {
+ return EXIT_SUCCESS;
+ }
/* redirect our output to the emulator console */
if (console) {
@@ -615,36 +642,34 @@
printStatistics = 1;
break;
+ case ':':
+ logcat_panic(true, "Option -%c needs an argument\n", optopt);
+ break;
+
default:
- fprintf(stderr,"Unrecognized Option\n");
- android::show_help(argv[0]);
- exit(-1);
- break;
+ logcat_panic(true, "Unrecognized Option %c\n", optopt);
+ break;
}
}
if (!devices) {
dev = devices = new log_device_t("main", false);
- android::g_devCount = 1;
+ g_devCount = 1;
if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
dev = dev->next = new log_device_t("system", false);
- android::g_devCount++;
+ g_devCount++;
}
if (android_name_to_log_id("crash") == LOG_ID_CRASH) {
dev = dev->next = new log_device_t("crash", false);
- android::g_devCount++;
+ g_devCount++;
}
}
- if (android::g_logRotateSizeKBytes != 0
- && android::g_outputFileName == NULL
- ) {
- fprintf(stderr,"-r requires -f as well\n");
- android::show_help(argv[0]);
- exit(-1);
+ if (g_logRotateSizeKBytes != 0 && g_outputFileName == NULL) {
+ logcat_panic(true, "-r requires -f as well\n");
}
- android::setupOutput();
+ setupOutput();
if (hasSetLogFormat == 0) {
const char* logFormat = getenv("ANDROID_PRINTF_LOG");
@@ -663,8 +688,7 @@
if (forceFilters) {
err = android_log_addFilterString(g_logformat, forceFilters);
if (err < 0) {
- fprintf (stderr, "Invalid filter expression in -logcat option\n");
- exit(0);
+ logcat_panic(false, "Invalid filter expression in logcat args\n");
}
} else if (argc == optind) {
// Add from environment variable
@@ -674,10 +698,8 @@
err = android_log_addFilterString(g_logformat, env_tags_orig);
if (err < 0) {
- fprintf(stderr, "Invalid filter expression in"
- " ANDROID_LOG_TAGS\n");
- android::show_help(argv[0]);
- exit(-1);
+ logcat_panic(true,
+ "Invalid filter expression in ANDROID_LOG_TAGS\n");
}
}
} else {
@@ -686,9 +708,7 @@
err = android_log_addFilterString(g_logformat, argv[i]);
if (err < 0) {
- fprintf (stderr, "Invalid filter expression '%s'\n", argv[i]);
- android::show_help(argv[0]);
- exit(-1);
+ logcat_panic(true, "Invalid filter expression '%s'\n", argv[i]);
}
}
}
@@ -704,22 +724,20 @@
dev->logger = android_logger_open(logger_list,
android_name_to_log_id(dev->device));
if (!dev->logger) {
- fprintf(stderr, "Unable to open log device '%s'\n", dev->device);
- exit(EXIT_FAILURE);
+ logcat_panic(false, "Unable to open log device '%s'\n",
+ dev->device);
}
if (clearLog) {
int ret;
ret = android_logger_clear(dev->logger);
if (ret) {
- perror("failed to clear the log");
- exit(EXIT_FAILURE);
+ logcat_panic(false, "failed to clear the log");
}
}
if (setLogSize && android_logger_set_log_size(dev->logger, setLogSize)) {
- perror("failed to set the log size");
- exit(EXIT_FAILURE);
+ logcat_panic(false, "failed to set the log size");
}
if (getLogSize) {
@@ -727,14 +745,12 @@
size = android_logger_get_log_size(dev->logger);
if (size < 0) {
- perror("failed to get the log size");
- exit(EXIT_FAILURE);
+ logcat_panic(false, "failed to get the log size");
}
readable = android_logger_get_log_readable_size(dev->logger);
if (readable < 0) {
- perror("failed to get the readable log size");
- exit(EXIT_FAILURE);
+ logcat_panic(false, "failed to get the readable log size");
}
printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
@@ -748,16 +764,18 @@
}
if (setPruneList) {
- size_t len = strlen(setPruneList) + 32; // margin to allow rc
- char *buf = (char *) malloc(len);
-
- strcpy(buf, setPruneList);
- int ret = android_logger_set_prune_list(logger_list, buf, len);
- free(buf);
-
- if (ret) {
- perror("failed to set the prune list");
- exit(EXIT_FAILURE);
+ size_t len = strlen(setPruneList);
+ /*extra 32 bytes are needed by android_logger_set_prune_list */
+ size_t bLen = len + 32;
+ char *buf = NULL;
+ if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
+ buf[len] = '\0';
+ if (android_logger_set_prune_list(logger_list, buf, bLen)) {
+ logcat_panic(false, "failed to set the prune list");
+ }
+ free(buf);
+ } else {
+ logcat_panic(false, "failed to set the prune list (alloc)");
}
}
@@ -767,29 +785,28 @@
for(int retry = 32;
(retry >= 0) && ((buf = new char [len]));
- delete [] buf, --retry) {
+ delete [] buf, buf = NULL, --retry) {
if (getPruneList) {
android_logger_get_prune_list(logger_list, buf, len);
} else {
android_logger_get_statistics(logger_list, buf, len);
}
buf[len-1] = '\0';
- size_t ret = atol(buf) + 1;
- if (ret < 4) {
+ if (atol(buf) < 3) {
delete [] buf;
buf = NULL;
break;
}
- bool check = ret <= len;
- len = ret;
- if (check) {
+ size_t ret = atol(buf) + 1;
+ if (ret <= len) {
+ len = ret;
break;
}
+ len = ret;
}
if (!buf) {
- perror("failed to read data");
- exit(EXIT_FAILURE);
+ logcat_panic(false, "failed to read data");
}
// remove trailing FF
@@ -813,18 +830,18 @@
printf("%s", cp);
delete [] buf;
- exit(0);
+ return EXIT_SUCCESS;
}
if (getLogSize) {
- exit(0);
+ return EXIT_SUCCESS;
}
if (setLogSize || setPruneList) {
- exit(0);
+ return EXIT_SUCCESS;
}
if (clearLog) {
- exit(0);
+ return EXIT_SUCCESS;
}
//LOG_EVENT_INT(10, 12345);
@@ -839,8 +856,7 @@
int ret = android_logger_list_read(logger_list, &log_msg);
if (ret == 0) {
- fprintf(stderr, "read: unexpected EOF!\n");
- exit(EXIT_FAILURE);
+ logcat_panic(false, "read: unexpected EOF!\n");
}
if (ret < 0) {
@@ -849,15 +865,12 @@
}
if (ret == -EIO) {
- fprintf(stderr, "read: unexpected EOF!\n");
- exit(EXIT_FAILURE);
+ logcat_panic(false, "read: unexpected EOF!\n");
}
if (ret == -EINVAL) {
- fprintf(stderr, "read: unexpected length.\n");
- exit(EXIT_FAILURE);
+ logcat_panic(false, "read: unexpected length.\n");
}
- perror("logcat read failure");
- exit(EXIT_FAILURE);
+ logcat_panic(false, "logcat read failure");
}
for(d = devices; d; d = d->next) {
@@ -866,23 +879,23 @@
}
}
if (!d) {
- android::g_devCount = 2; // set to Multiple
+ g_devCount = 2; // set to Multiple
d = &unexpected;
d->binary = log_msg.id() == LOG_ID_EVENTS;
}
if (dev != d) {
dev = d;
- android::maybePrintStart(dev, printDividers);
+ maybePrintStart(dev, printDividers);
}
- if (android::g_printBinary) {
- android::printBinary(&log_msg);
+ if (g_printBinary) {
+ printBinary(&log_msg);
} else {
- android::processBuffer(dev, &log_msg);
+ processBuffer(dev, &log_msg);
}
}
android_logger_list_free(logger_list);
- return 0;
+ return EXIT_SUCCESS;
}
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index 015a23d..a28664e 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -39,7 +39,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE := $(test_module_prefix)benchmarks
LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_CFLAGS += $(test_c_flags)
LOCAL_SRC_FILES := $(benchmark_src_files)
include $(BUILD_NATIVE_TEST)
@@ -56,7 +55,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE := $(test_module_prefix)unit-tests
LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_CFLAGS += $(test_c_flags)
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_SRC_FILES := $(test_src_files)
diff --git a/logd/Android.mk b/logd/Android.mk
index 127a66b..73da8dc 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -18,6 +18,7 @@
LogWhiteBlackList.cpp \
libaudit.c \
LogAudit.cpp \
+ LogKlog.cpp \
event.logtags
LOCAL_SHARED_LIBRARIES := \
@@ -32,8 +33,9 @@
# "s/^\([0-9]*\)[ \t]*$1[ \t].*/-D`echo $1 | tr a-z A-Z`_LOG_TAG=\1/p" \
# $(LOCAL_PATH)/$2/event.logtags)
# event_flag := $(call event_logtags,auditd)
+# event_flag += $(call event_logtags,logd)
# so make sure we do not regret hard-coding it as follows:
-event_flag := -DAUDITD_LOG_TAG=1003
+event_flag := -DAUDITD_LOG_TAG=1003 -DLOGD_LOG_TAG=1004
LOCAL_CFLAGS := -Werror $(event_flag)
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 561ea3e..5489cc9 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -33,9 +33,9 @@
#include "LogCommand.h"
CommandListener::CommandListener(LogBuffer *buf, LogReader * /*reader*/,
- LogListener * /*swl*/)
- : FrameworkListener(getLogSocket())
- , mBuf(*buf) {
+ LogListener * /*swl*/) :
+ FrameworkListener(getLogSocket()),
+ mBuf(*buf) {
// registerCmd(new ShutdownCmd(buf, writer, swl));
registerCmd(new ClearCmd(buf));
registerCmd(new GetBufSizeCmd(buf));
@@ -48,12 +48,12 @@
}
CommandListener::ShutdownCmd::ShutdownCmd(LogBuffer *buf, LogReader *reader,
- LogListener *swl)
- : LogCommand("shutdown")
- , mBuf(*buf)
- , mReader(*reader)
- , mSwl(*swl)
-{ }
+ LogListener *swl) :
+ LogCommand("shutdown"),
+ mBuf(*buf),
+ mReader(*reader),
+ mSwl(*swl) {
+}
int CommandListener::ShutdownCmd::runCommand(SocketClient * /*cli*/,
int /*argc*/,
@@ -63,13 +63,17 @@
exit(0);
}
-CommandListener::ClearCmd::ClearCmd(LogBuffer *buf)
- : LogCommand("clear")
- , mBuf(*buf)
-{ }
+CommandListener::ClearCmd::ClearCmd(LogBuffer *buf) :
+ LogCommand("clear"),
+ mBuf(*buf) {
+}
static void setname() {
- prctl(PR_SET_NAME, "logd.control");
+ static bool name_set;
+ if (!name_set) {
+ prctl(PR_SET_NAME, "logd.control");
+ name_set = true;
+ }
}
int CommandListener::ClearCmd::runCommand(SocketClient *cli,
@@ -96,10 +100,10 @@
return 0;
}
-CommandListener::GetBufSizeCmd::GetBufSizeCmd(LogBuffer *buf)
- : LogCommand("getLogSize")
- , mBuf(*buf)
-{ }
+CommandListener::GetBufSizeCmd::GetBufSizeCmd(LogBuffer *buf) :
+ LogCommand("getLogSize"),
+ mBuf(*buf) {
+}
int CommandListener::GetBufSizeCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
@@ -122,10 +126,10 @@
return 0;
}
-CommandListener::SetBufSizeCmd::SetBufSizeCmd(LogBuffer *buf)
- : LogCommand("setLogSize")
- , mBuf(*buf)
-{ }
+CommandListener::SetBufSizeCmd::SetBufSizeCmd(LogBuffer *buf) :
+ LogCommand("setLogSize"),
+ mBuf(*buf) {
+}
int CommandListener::SetBufSizeCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
@@ -156,10 +160,10 @@
return 0;
}
-CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer *buf)
- : LogCommand("getLogSizeUsed")
- , mBuf(*buf)
-{ }
+CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer *buf) :
+ LogCommand("getLogSizeUsed"),
+ mBuf(*buf) {
+}
int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
@@ -182,10 +186,10 @@
return 0;
}
-CommandListener::GetStatisticsCmd::GetStatisticsCmd(LogBuffer *buf)
- : LogCommand("getStatistics")
- , mBuf(*buf)
-{ }
+CommandListener::GetStatisticsCmd::GetStatisticsCmd(LogBuffer *buf) :
+ LogCommand("getStatistics"),
+ mBuf(*buf) {
+}
static void package_string(char **strp) {
const char *a = *strp;
@@ -239,10 +243,10 @@
return 0;
}
-CommandListener::GetPruneListCmd::GetPruneListCmd(LogBuffer *buf)
- : LogCommand("getPruneList")
- , mBuf(*buf)
-{ }
+CommandListener::GetPruneListCmd::GetPruneListCmd(LogBuffer *buf) :
+ LogCommand("getPruneList"),
+ mBuf(*buf) {
+}
int CommandListener::GetPruneListCmd::runCommand(SocketClient *cli,
int /*argc*/, char ** /*argv*/) {
@@ -259,10 +263,10 @@
return 0;
}
-CommandListener::SetPruneListCmd::SetPruneListCmd(LogBuffer *buf)
- : LogCommand("setPruneList")
- , mBuf(*buf)
-{ }
+CommandListener::SetPruneListCmd::SetPruneListCmd(LogBuffer *buf) :
+ LogCommand("setPruneList"),
+ mBuf(*buf) {
+}
int CommandListener::SetPruneListCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
@@ -297,9 +301,8 @@
return 0;
}
-CommandListener::ReinitCmd::ReinitCmd()
- : LogCommand("reinit")
-{ }
+CommandListener::ReinitCmd::ReinitCmd() : LogCommand("reinit") {
+}
int CommandListener::ReinitCmd::runCommand(SocketClient *cli,
int /*argc*/, char ** /*argv*/) {
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
index 26a1861..d584925 100644
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -27,14 +27,14 @@
unsigned long tail,
unsigned int logMask,
pid_t pid,
- uint64_t start)
- : mReader(reader)
- , mNonBlock(nonBlock)
- , mTail(tail)
- , mLogMask(logMask)
- , mPid(pid)
- , mStart(start)
-{ }
+ uint64_t start) :
+ mReader(reader),
+ mNonBlock(nonBlock),
+ mTail(tail),
+ mLogMask(logMask),
+ mPid(pid),
+ mStart(start) {
+}
// runSocketCommand is called once for every open client on the
// log reader socket. Here we manage and associated the reader
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 6b3e637..4ec2e59 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -24,10 +24,12 @@
#include <sys/uio.h>
#include <syslog.h>
+#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#include "libaudit.h"
#include "LogAudit.h"
+#include "LogKlog.h"
#define KMSG_PRIORITY(PRI) \
'<', \
@@ -35,12 +37,12 @@
'0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) % 10, \
'>'
-LogAudit::LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg)
- : SocketListener(getLogSocket(), false)
- , logbuf(buf)
- , reader(reader)
- , fdDmesg(fdDmesg)
- , initialized(false) {
+LogAudit::LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg) :
+ SocketListener(getLogSocket(), false),
+ logbuf(buf),
+ reader(reader),
+ fdDmesg(fdDmesg),
+ initialized(false) {
static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
'l', 'o', 'g', 'd', '.', 'a', 'u', 'd', 'i', 't', 'd', ':',
' ', 's', 't', 'a', 'r', 't', '\n' };
@@ -110,7 +112,7 @@
pid_t pid = getpid();
pid_t tid = gettid();
- uid_t uid = getuid();
+ uid_t uid = AID_LOGD;
log_time now;
static const char audit_str[] = " audit(";
@@ -120,6 +122,15 @@
&& (*cp == ':')) {
memcpy(timeptr + sizeof(audit_str) - 1, "0.0", 3);
memmove(timeptr + sizeof(audit_str) - 1 + 3, cp, strlen(cp) + 1);
+ //
+ // We are either in 1970ish (MONOTONIC) or 2015+ish (REALTIME) so to
+ // differentiate without prejudice, we use 1980 to delineate, earlier
+ // is monotonic, later is real.
+ //
+# define EPOCH_PLUS_10_YEARS (10 * 1461 / 4 * 24 * 60 * 60)
+ if (now.tv_sec < EPOCH_PLUS_10_YEARS) {
+ LogKlog::convertMonotonicToReal(now);
+ }
} else {
now.strptime("", ""); // side effect of setting CLOCK_REALTIME
}
@@ -150,16 +161,18 @@
rc = -ENOMEM;
} else {
event->header.tag = htole32(AUDITD_LOG_TAG);
- event->payload.type = EVENT_TYPE_STRING;
- event->payload.length = htole32(l);
- memcpy(event->payload.data, str, l);
+ event->type = EVENT_TYPE_STRING;
+ event->length = htole32(l);
+ memcpy(event->data, str, l);
- logbuf->log(LOG_ID_EVENTS, now, uid, pid, tid,
- reinterpret_cast<char *>(event),
- (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX);
+ rc = logbuf->log(LOG_ID_EVENTS, now, uid, pid, tid,
+ reinterpret_cast<char *>(event),
+ (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX);
free(event);
- notify = true;
+ if (rc >= 0) {
+ notify = true;
+ }
}
// log to main
@@ -196,17 +209,22 @@
strncpy(newstr + 1 + l, str, estr - str);
strcpy(newstr + 1 + l + (estr - str), ecomm);
- logbuf->log(LOG_ID_MAIN, now, uid, pid, tid, newstr,
- (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX);
+ rc = logbuf->log(LOG_ID_MAIN, now, uid, pid, tid, newstr,
+ (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX);
free(newstr);
- notify = true;
+ if (rc >= 0) {
+ notify = true;
+ }
}
free(str);
if (notify) {
reader->notifyNewLog();
+ if (rc < 0) {
+ rc = n;
+ }
}
return rc;
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 2693583..c33dca6 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -15,8 +15,8 @@
*/
#include <ctype.h>
+#include <errno.h>
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
#include <sys/user.h>
#include <time.h>
@@ -27,8 +27,6 @@
#include "LogBuffer.h"
#include "LogReader.h"
-#include "LogStatistics.h"
-#include "LogWhiteBlackList.h"
// Default
#define LOG_BUFFER_SIZE (256 * 1024) // Tuned on a per-platform basis here?
@@ -128,18 +126,17 @@
}
}
-LogBuffer::LogBuffer(LastLogTimes *times)
- : mTimes(*times) {
+LogBuffer::LogBuffer(LastLogTimes *times) : mTimes(*times) {
pthread_mutex_init(&mLogElementsLock, NULL);
init();
}
-void LogBuffer::log(log_id_t log_id, log_time realtime,
- uid_t uid, pid_t pid, pid_t tid,
- const char *msg, unsigned short len) {
+int LogBuffer::log(log_id_t log_id, log_time realtime,
+ uid_t uid, pid_t pid, pid_t tid,
+ const char *msg, unsigned short len) {
if ((log_id >= LOG_ID_MAX) || (log_id < 0)) {
- return;
+ return -EINVAL;
}
LogBufferElement *elem = new LogBufferElement(log_id, realtime,
uid, pid, tid, msg, len);
@@ -193,9 +190,11 @@
LogTimeEntry::unlock();
}
- stats.add(len, log_id, uid, pid);
+ stats.add(elem);
maybePrune(log_id);
pthread_mutex_unlock(&mLogElementsLock);
+
+ return len;
}
// If we're using more than 256K of memory for log entries, prune
@@ -216,6 +215,95 @@
}
}
+LogBufferElementCollection::iterator LogBuffer::erase(LogBufferElementCollection::iterator it) {
+ LogBufferElement *e = *it;
+
+ it = mLogElements.erase(it);
+ stats.subtract(e);
+ delete e;
+
+ return it;
+}
+
+// Define a temporary mechanism to report the last LogBufferElement pointer
+// for the specified uid, pid and tid. Used below to help merge-sort when
+// pruning for worst UID.
+class LogBufferElementKey {
+ const union {
+ struct {
+ uint16_t uid;
+ uint16_t pid;
+ uint16_t tid;
+ uint16_t padding;
+ } __packed;
+ uint64_t value;
+ } __packed;
+
+public:
+ LogBufferElementKey(uid_t u, pid_t p, pid_t t):uid(u),pid(p),tid(t),padding(0) { }
+ LogBufferElementKey(uint64_t k):value(k) { }
+
+ uint64_t getKey() { return value; }
+};
+
+class LogBufferElementEntry {
+ const uint64_t key;
+ LogBufferElement *last;
+
+public:
+ LogBufferElementEntry(const uint64_t &k, LogBufferElement *e):key(k),last(e) { }
+
+ const uint64_t&getKey() const { return key; }
+
+ LogBufferElement *getLast() { return last; }
+};
+
+class LogBufferElementLast : public android::BasicHashtable<uint64_t, LogBufferElementEntry> {
+
+public:
+ bool merge(LogBufferElement *e, unsigned short dropped) {
+ LogBufferElementKey key(e->getUid(), e->getPid(), e->getTid());
+ android::hash_t hash = android::hash_type(key.getKey());
+ ssize_t index = find(-1, hash, key.getKey());
+ if (index != -1) {
+ LogBufferElementEntry &entry = editEntryAt(index);
+ LogBufferElement *l = entry.getLast();
+ unsigned short d = l->getDropped();
+ if ((dropped + d) > USHRT_MAX) {
+ removeAt(index);
+ } else {
+ l->setDropped(dropped + d);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ size_t add(LogBufferElement *e) {
+ LogBufferElementKey key(e->getUid(), e->getPid(), e->getTid());
+ android::hash_t hash = android::hash_type(key.getKey());
+ return android::BasicHashtable<uint64_t, LogBufferElementEntry>::
+ add(hash, LogBufferElementEntry(key.getKey(), e));
+ }
+
+ inline void clear() {
+ android::BasicHashtable<uint64_t, LogBufferElementEntry>::clear();
+ }
+
+ void clear(LogBufferElement *e) {
+ uint64_t current = e->getRealTime().nsec() - NS_PER_SEC;
+ ssize_t index = -1;
+ while((index = next(index)) >= 0) {
+ LogBufferElement *l = editEntryAt(index).getLast();
+ if ((l->getDropped() >= 4) && (current > l->getRealTime().nsec())) {
+ removeAt(index);
+ index = -1;
+ }
+ }
+ }
+
+};
+
// prune "pruneRows" of type "id" from the buffer.
//
// mLogElementsLock must be held when this function is called.
@@ -250,12 +338,8 @@
continue;
}
- uid_t uid = e->getUid();
-
- if (uid == caller_uid) {
- it = mLogElements.erase(it);
- stats.subtract(e->getMsgLen(), id, uid, e->getPid());
- delete e;
+ if (e->getUid() == caller_uid) {
+ it = erase(it);
pruneRows--;
if (pruneRows == 0) {
break;
@@ -269,27 +353,40 @@
}
// prune by worst offender by uid
+ bool hasBlacklist = mPrune.naughty();
while (pruneRows > 0) {
// recalculate the worst offender on every batched pass
uid_t worst = (uid_t) -1;
size_t worst_sizes = 0;
size_t second_worst_sizes = 0;
- if ((id != LOG_ID_CRASH) && mPrune.worstUidEnabled()) {
- LidStatistics &l = stats.id(id);
- l.sort();
- UidStatisticsCollection::iterator iu = l.begin();
- if (iu != l.end()) {
- UidStatistics *u = *iu;
- worst = u->getUid();
- worst_sizes = u->sizes();
- if (++iu != l.end()) {
- second_worst_sizes = (*iu)->sizes();
+ if (worstUidEnabledForLogid(id) && mPrune.worstUidEnabled()) {
+ std::unique_ptr<const UidEntry *[]> sorted = stats.sort(2, id);
+
+ if (sorted.get()) {
+ if (sorted[0] && sorted[1]) {
+ worst_sizes = sorted[0]->getSizes();
+ // Calculate threshold as 12.5% of available storage
+ size_t threshold = log_buffer_size(id) / 8;
+ if (worst_sizes > threshold) {
+ worst = sorted[0]->getKey();
+ second_worst_sizes = sorted[1]->getSizes();
+ if (second_worst_sizes < threshold) {
+ second_worst_sizes = threshold;
+ }
+ }
}
}
}
+ // skip if we have neither worst nor naughty filters
+ if ((worst == (uid_t) -1) && !hasBlacklist) {
+ break;
+ }
+
bool kick = false;
+ bool leading = true;
+ LogBufferElementLast last;
for(it = mLogElements.begin(); it != mLogElements.end();) {
LogBufferElement *e = *it;
@@ -302,27 +399,82 @@
continue;
}
- uid_t uid = e->getUid();
+ unsigned short dropped = e->getDropped();
- if ((uid == worst) || mPrune.naughty(e)) { // Worst or BlackListed
+ // remove any leading drops
+ if (leading && dropped) {
+ it = erase(it);
+ continue;
+ }
+
+ // merge any drops
+ if (dropped && last.merge(e, dropped)) {
it = mLogElements.erase(it);
- unsigned short len = e->getMsgLen();
- stats.subtract(len, id, uid, e->getPid());
+ stats.erase(e);
delete e;
+ continue;
+ }
+
+ leading = false;
+
+ if (hasBlacklist && mPrune.naughty(e)) {
+ last.clear(e);
+ it = erase(it);
+ if (dropped) {
+ continue;
+ }
+
pruneRows--;
- if (uid == worst) {
- kick = true;
- if ((pruneRows == 0) || (worst_sizes < second_worst_sizes)) {
- break;
- }
- worst_sizes -= len;
- } else if (pruneRows == 0) {
+ if (pruneRows == 0) {
break;
}
+
+ if (e->getUid() == worst) {
+ kick = true;
+ if (worst_sizes < second_worst_sizes) {
+ break;
+ }
+ worst_sizes -= e->getMsgLen();
+ }
+ continue;
+ }
+
+ if (dropped) {
+ last.add(e);
+ ++it;
+ continue;
+ }
+
+ if (e->getUid() != worst) {
+ last.clear(e);
+ ++it;
+ continue;
+ }
+
+ pruneRows--;
+ if (pruneRows == 0) {
+ break;
+ }
+
+ kick = true;
+
+ unsigned short len = e->getMsgLen();
+ stats.drop(e);
+ e->setDropped(1);
+ if (last.merge(e, 1)) {
+ it = mLogElements.erase(it);
+ stats.erase(e);
+ delete e;
} else {
+ last.add(e);
++it;
}
+ if (worst_sizes < second_worst_sizes) {
+ break;
+ }
+ worst_sizes -= len;
}
+ last.clear();
if (!kick || !mPrune.worstUidEnabled()) {
break; // the following loop will ask bad clients to skip/drop
@@ -330,58 +482,63 @@
}
bool whitelist = false;
+ bool hasWhitelist = mPrune.nice();
it = mLogElements.begin();
while((pruneRows > 0) && (it != mLogElements.end())) {
LogBufferElement *e = *it;
- if (e->getLogId() == id) {
- if (oldest && (oldest->mStart <= e->getSequence())) {
- if (!whitelist) {
- if (stats.sizes(id) > (2 * log_buffer_size(id))) {
- // kick a misbehaving log reader client off the island
- oldest->release_Locked();
- } else {
- oldest->triggerSkip_Locked(id, pruneRows);
- }
- }
+
+ if (e->getLogId() != id) {
+ it++;
+ continue;
+ }
+
+ if (oldest && (oldest->mStart <= e->getSequence())) {
+ if (whitelist) {
break;
}
- if (mPrune.nice(e)) { // WhiteListed
- whitelist = true;
- it++;
- continue;
+ if (stats.sizes(id) > (2 * log_buffer_size(id))) {
+ // kick a misbehaving log reader client off the island
+ oldest->release_Locked();
+ } else {
+ oldest->triggerSkip_Locked(id, pruneRows);
}
-
- it = mLogElements.erase(it);
- stats.subtract(e->getMsgLen(), id, e->getUid(), e->getPid());
- delete e;
- pruneRows--;
- } else {
- it++;
+ break;
}
+
+ if (hasWhitelist && mPrune.nice(e)) { // WhiteListed
+ whitelist = true;
+ it++;
+ continue;
+ }
+
+ it = erase(it);
+ pruneRows--;
}
+ // Do not save the whitelist if we are reader range limited
if (whitelist && (pruneRows > 0)) {
it = mLogElements.begin();
while((it != mLogElements.end()) && (pruneRows > 0)) {
LogBufferElement *e = *it;
- if (e->getLogId() == id) {
- if (oldest && (oldest->mStart <= e->getSequence())) {
- if (stats.sizes(id) > (2 * log_buffer_size(id))) {
- // kick a misbehaving log reader client off the island
- oldest->release_Locked();
- } else {
- oldest->triggerSkip_Locked(id, pruneRows);
- }
- break;
- }
- it = mLogElements.erase(it);
- stats.subtract(e->getMsgLen(), id, e->getUid(), e->getPid());
- delete e;
- pruneRows--;
- } else {
- it++;
+
+ if (e->getLogId() != id) {
+ ++it;
+ continue;
}
+
+ if (oldest && (oldest->mStart <= e->getSequence())) {
+ if (stats.sizes(id) > (2 * log_buffer_size(id))) {
+ // kick a misbehaving log reader client off the island
+ oldest->release_Locked();
+ } else {
+ oldest->triggerSkip_Locked(id, pruneRows);
+ }
+ break;
+ }
+
+ it = erase(it);
+ pruneRows--;
}
}
@@ -473,7 +630,7 @@
pthread_mutex_unlock(&mLogElementsLock);
// range locking in LastLogTimes looks after us
- max = element->flushTo(reader);
+ max = element->flushTo(reader, this);
if (max == element->FLUSH_ERROR) {
return max;
@@ -487,22 +644,9 @@
}
void LogBuffer::formatStatistics(char **strp, uid_t uid, unsigned int logMask) {
- uint64_t oldest = UINT64_MAX;
-
pthread_mutex_lock(&mLogElementsLock);
- // Find oldest element in the log(s)
- LogBufferElementCollection::iterator it;
- for (it = mLogElements.begin(); it != mLogElements.end(); ++it) {
- LogBufferElement *element = *it;
-
- if ((logMask & (1 << element->getLogId()))) {
- oldest = element->getSequence();
- break;
- }
- }
-
- stats.format(strp, uid, logMask, oldest);
+ stats.format(strp, uid, logMask);
pthread_mutex_unlock(&mLogElementsLock);
}
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 13e6aa8..00b19b6 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -48,9 +48,9 @@
LogBuffer(LastLogTimes *times);
void init();
- void log(log_id_t log_id, log_time realtime,
- uid_t uid, pid_t pid, pid_t tid,
- const char *msg, unsigned short len);
+ int log(log_id_t log_id, log_time realtime,
+ uid_t uid, pid_t pid, pid_t tid,
+ const char *msg, unsigned short len);
uint64_t flushTo(SocketClient *writer, const uint64_t start,
bool privileged,
int (*filter)(const LogBufferElement *element, void *arg) = NULL,
@@ -74,11 +74,12 @@
// helper
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); }
private:
void maybePrune(log_id_t id);
void prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
-
+ LogBufferElementCollection::iterator erase(LogBufferElementCollection::iterator it);
};
#endif // _LOGD_LOG_BUFFER_H__
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 5e780b5..3d7237e 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -14,14 +14,19 @@
* limitations under the License.
*/
+#include <ctype.h>
+#include <endian.h>
+#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <log/logger.h>
+#include <private/android_logger.h>
#include "LogBufferElement.h"
+#include "LogCommand.h"
#include "LogReader.h"
const uint64_t LogBufferElement::FLUSH_ERROR(0);
@@ -29,14 +34,14 @@
LogBufferElement::LogBufferElement(log_id_t log_id, log_time realtime,
uid_t uid, pid_t pid, pid_t tid,
- const char *msg, unsigned short len)
- : mLogId(log_id)
- , mUid(uid)
- , mPid(pid)
- , mTid(tid)
- , mMsgLen(len)
- , mSequence(sequence.fetch_add(1, memory_order_relaxed))
- , mRealTime(realtime) {
+ const char *msg, unsigned short len) :
+ mLogId(log_id),
+ mUid(uid),
+ mPid(pid),
+ mTid(tid),
+ mMsgLen(len),
+ mSequence(sequence.fetch_add(1, memory_order_relaxed)),
+ mRealTime(realtime) {
mMsg = new char[len];
memcpy(mMsg, msg, len);
}
@@ -45,11 +50,138 @@
delete [] mMsg;
}
-uint64_t LogBufferElement::flushTo(SocketClient *reader) {
+uint32_t LogBufferElement::getTag() const {
+ if ((mLogId != LOG_ID_EVENTS) || !mMsg || (mMsgLen < sizeof(uint32_t))) {
+ return 0;
+ }
+ return le32toh(reinterpret_cast<android_event_header_t *>(mMsg)->tag);
+}
+
+// caller must own and free character string
+char *android::tidToName(pid_t tid) {
+ char *retval = NULL;
+ char buffer[256];
+ snprintf(buffer, sizeof(buffer), "/proc/%u/comm", tid);
+ int fd = open(buffer, O_RDONLY);
+ if (fd >= 0) {
+ ssize_t ret = read(fd, buffer, sizeof(buffer));
+ if (ret >= (ssize_t)sizeof(buffer)) {
+ ret = sizeof(buffer) - 1;
+ }
+ while ((ret > 0) && isspace(buffer[ret - 1])) {
+ --ret;
+ }
+ if (ret > 0) {
+ buffer[ret] = '\0';
+ retval = strdup(buffer);
+ }
+ close(fd);
+ }
+
+ // if nothing for comm, check out cmdline
+ char *name = android::pidToName(tid);
+ if (!retval) {
+ retval = name;
+ name = NULL;
+ }
+
+ // check if comm is truncated, see if cmdline has full representation
+ if (name) {
+ // impossible for retval to be NULL if name not NULL
+ size_t retval_len = strlen(retval);
+ size_t name_len = strlen(name);
+ // KISS: ToDo: Only checks prefix truncated, not suffix, or both
+ if ((retval_len < name_len) && !strcmp(retval, name + name_len - retval_len)) {
+ free(retval);
+ retval = name;
+ } else {
+ free(name);
+ }
+ }
+ return retval;
+}
+
+// assumption: mMsg == NULL
+size_t LogBufferElement::populateDroppedMessage(char *&buffer,
+ LogBuffer *parent) {
+ static const char tag[] = "logd";
+ static const char format_uid[] = "uid=%u%s too chatty%s, expire %u line%s";
+
+ char *name = parent->uidToName(mUid);
+ char *commName = android::tidToName(mTid);
+ if (!commName && (mTid != mPid)) {
+ commName = android::tidToName(mPid);
+ }
+ if (!commName) {
+ commName = parent->pidToName(mPid);
+ }
+ if (name && commName && !strcmp(name, commName)) {
+ free(commName);
+ commName = NULL;
+ }
+ if (name) {
+ char *p = NULL;
+ asprintf(&p, "(%s)", name);
+ if (p) {
+ free(name);
+ name = p;
+ }
+ }
+ if (commName) {
+ char *p = NULL;
+ asprintf(&p, " comm=%s", commName);
+ if (p) {
+ free(commName);
+ commName = p;
+ }
+ }
+ // identical to below to calculate the buffer size required
+ size_t len = snprintf(NULL, 0, format_uid, mUid, name ? name : "",
+ commName ? commName : "",
+ mDropped, (mDropped > 1) ? "s" : "");
+
+ size_t hdrLen;
+ if (mLogId == LOG_ID_EVENTS) {
+ hdrLen = sizeof(android_log_event_string_t);
+ } else {
+ hdrLen = 1 + sizeof(tag);
+ }
+
+ buffer = static_cast<char *>(calloc(1, hdrLen + len + 1));
+ if (!buffer) {
+ free(name);
+ free(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);
+
+ e->header.tag = htole32(LOGD_LOG_TAG);
+ e->type = EVENT_TYPE_STRING;
+ e->length = htole32(len);
+ } else {
+ ++retval;
+ buffer[0] = ANDROID_LOG_INFO;
+ strcpy(buffer + 1, tag);
+ }
+
+ snprintf(buffer + hdrLen, len + 1, format_uid, mUid, name ? name : "",
+ commName ? commName : "",
+ mDropped, (mDropped > 1) ? "s" : "");
+ free(name);
+ free(commName);
+
+ return retval;
+}
+
+uint64_t LogBufferElement::flushTo(SocketClient *reader, LogBuffer *parent) {
struct logger_entry_v3 entry;
+
memset(&entry, 0, sizeof(struct logger_entry_v3));
+
entry.hdr_size = sizeof(struct logger_entry_v3);
- entry.len = mMsgLen;
entry.lid = mLogId;
entry.pid = mPid;
entry.tid = mTid;
@@ -59,11 +191,26 @@
struct iovec iovec[2];
iovec[0].iov_base = &entry;
iovec[0].iov_len = sizeof(struct logger_entry_v3);
- iovec[1].iov_base = mMsg;
- iovec[1].iov_len = mMsgLen;
- if (reader->sendDatav(iovec, 2)) {
- return FLUSH_ERROR;
+
+ char *buffer = NULL;
+
+ if (!mMsg) {
+ entry.len = populateDroppedMessage(buffer, parent);
+ if (!entry.len) {
+ return mSequence;
+ }
+ iovec[1].iov_base = buffer;
+ } else {
+ entry.len = mMsgLen;
+ iovec[1].iov_base = mMsg;
+ }
+ iovec[1].iov_len = entry.len;
+
+ uint64_t retval = reader->sendDatav(iovec, 2) ? FLUSH_ERROR : mSequence;
+
+ if (buffer) {
+ free(buffer);
}
- return mSequence;
+ return retval;
}
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 25f1450..5dabaac 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -17,23 +17,55 @@
#ifndef _LOGD_LOG_BUFFER_ELEMENT_H__
#define _LOGD_LOG_BUFFER_ELEMENT_H__
-#include <sys/types.h>
#include <stdatomic.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
#include <sysutils/SocketClient.h>
#include <log/log.h>
#include <log/log_read.h>
+// Hijack this header as a common include file used by most all sources
+// to report some utilities defined here and there.
+
+namespace android {
+
+// Furnished in main.cpp. Caller must own and free returned value
+char *uidToName(uid_t uid);
+
+// Furnished in LogStatistics.cpp. Caller must own and free returned value
+char *pidToName(pid_t pid);
+char *tidToName(pid_t tid);
+
+// Furnished in main.cpp. Thread safe.
+const char *tagToName(uint32_t tag);
+
+}
+
+static inline bool worstUidEnabledForLogid(log_id_t id) {
+ return (id != LOG_ID_CRASH) && (id != LOG_ID_KERNEL) && (id != LOG_ID_EVENTS);
+}
+
+class LogBuffer;
+
class LogBufferElement {
const log_id_t mLogId;
const uid_t mUid;
const pid_t mPid;
const pid_t mTid;
char *mMsg;
- const unsigned short mMsgLen;
+ union {
+ const unsigned short mMsgLen; // mMSg != NULL
+ unsigned short mDropped; // mMsg == NULL
+ };
const uint64_t mSequence;
const log_time mRealTime;
static atomic_int_fast64_t sequence;
+ // assumption: mMsg == NULL
+ size_t populateDroppedMessage(char *&buffer,
+ LogBuffer *parent);
+
public:
LogBufferElement(log_id_t log_id, log_time realtime,
uid_t uid, pid_t pid, pid_t tid,
@@ -44,13 +76,23 @@
uid_t getUid(void) const { return mUid; }
pid_t getPid(void) const { return mPid; }
pid_t getTid(void) const { return mTid; }
- unsigned short getMsgLen() const { return mMsgLen; }
+ unsigned short getDropped(void) const { return mMsg ? 0 : mDropped; }
+ unsigned short setDropped(unsigned short value) {
+ if (mMsg) {
+ free(mMsg);
+ mMsg = NULL;
+ }
+ return mDropped = value;
+ }
+ unsigned short getMsgLen() const { return mMsg ? mMsgLen : 0; }
uint64_t getSequence(void) const { return mSequence; }
static uint64_t getCurrentSequence(void) { return sequence.load(memory_order_relaxed); }
log_time getRealTime(void) const { return mRealTime; }
+ uint32_t getTag(void) const;
+
static const uint64_t FLUSH_ERROR;
- uint64_t flushTo(SocketClient *writer);
+ uint64_t flushTo(SocketClient *writer, LogBuffer *parent);
};
#endif
diff --git a/logd/LogCommand.cpp b/logd/LogCommand.cpp
index e4c138e..06d865c 100644
--- a/logd/LogCommand.cpp
+++ b/logd/LogCommand.cpp
@@ -17,13 +17,13 @@
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <private/android_filesystem_config.h>
#include "LogCommand.h"
-LogCommand::LogCommand(const char *cmd)
- : FrameworkCommand(cmd) {
+LogCommand::LogCommand(const char *cmd) : FrameworkCommand(cmd) {
}
// gets a list of supplementary group IDs associated with
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
new file mode 100644
index 0000000..8df0d0a
--- /dev/null
+++ b/logd/LogKlog.cpp
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/prctl.h>
+#include <sys/uio.h>
+#include <syslog.h>
+
+#include <log/logger.h>
+
+#include "LogKlog.h"
+
+#define KMSG_PRIORITY(PRI) \
+ '<', \
+ '0' + (LOG_SYSLOG | (PRI)) / 10, \
+ '0' + (LOG_SYSLOG | (PRI)) % 10, \
+ '>'
+
+static const char priority_message[] = { KMSG_PRIORITY(LOG_INFO), '\0' };
+
+log_time LogKlog::correction = log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC);
+
+LogKlog::LogKlog(LogBuffer *buf, LogReader *reader, int fdWrite, int fdRead, bool auditd) :
+ SocketListener(fdRead, false),
+ logbuf(buf),
+ reader(reader),
+ signature(CLOCK_MONOTONIC),
+ fdWrite(fdWrite),
+ fdRead(fdRead),
+ initialized(false),
+ enableLogging(true),
+ auditd(auditd) {
+ static const char klogd_message[] = "%slogd.klogd: %" PRIu64 "\n";
+ char buffer[sizeof(priority_message) + sizeof(klogd_message) + 20 - 4];
+ snprintf(buffer, sizeof(buffer), klogd_message, priority_message,
+ signature.nsec());
+ write(fdWrite, buffer, strlen(buffer));
+}
+
+bool LogKlog::onDataAvailable(SocketClient *cli) {
+ if (!initialized) {
+ prctl(PR_SET_NAME, "logd.klogd");
+ initialized = true;
+ enableLogging = false;
+ }
+
+ char buffer[LOGGER_ENTRY_MAX_PAYLOAD];
+ size_t len = 0;
+
+ for(;;) {
+ ssize_t retval = 0;
+ if ((sizeof(buffer) - 1 - len) > 0) {
+ retval = read(cli->getSocket(), buffer + len, sizeof(buffer) - 1 - len);
+ }
+ if ((retval == 0) && (len == 0)) {
+ break;
+ }
+ if (retval < 0) {
+ return false;
+ }
+ len += retval;
+ bool full = len == (sizeof(buffer) - 1);
+ char *ep = buffer + len;
+ *ep = '\0';
+ len = 0;
+ for(char *ptr, *tok = buffer;
+ ((tok = strtok_r(tok, "\r\n", &ptr)));
+ tok = NULL) {
+ if (((tok + strlen(tok)) == ep) && (retval != 0) && full) {
+ len = strlen(tok);
+ memmove(buffer, tok, len);
+ break;
+ }
+ if (*tok) {
+ log(tok);
+ }
+ }
+ }
+
+ return true;
+}
+
+
+void LogKlog::calculateCorrection(const log_time &monotonic,
+ const char *real_string) {
+ log_time real;
+ if (!real.strptime(real_string, "%Y-%m-%d %H:%M:%S.%09q UTC")) {
+ return;
+ }
+ // kernel report UTC, log_time::strptime is localtime from calendar.
+ // Bionic and liblog strptime does not support %z or %Z to pick up
+ // timezone so we are calculating our own correction.
+ time_t now = real.tv_sec;
+ struct tm tm;
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_isdst = -1;
+ localtime_r(&now, &tm);
+ real.tv_sec += tm.tm_gmtoff;
+ correction = real - monotonic;
+}
+
+void LogKlog::sniffTime(log_time &now, const char **buf, bool reverse) {
+ const char *cp;
+ if ((cp = now.strptime(*buf, "[ %s.%q]"))) {
+ static const char suspend[] = "PM: suspend entry ";
+ static const char resume[] = "PM: suspend exit ";
+ static const char suspended[] = "Suspended for ";
+
+ if (isspace(*cp)) {
+ ++cp;
+ }
+ if (!strncmp(cp, suspend, sizeof(suspend) - 1)) {
+ calculateCorrection(now, cp + sizeof(suspend) - 1);
+ } else if (!strncmp(cp, resume, sizeof(resume) - 1)) {
+ calculateCorrection(now, cp + sizeof(resume) - 1);
+ } else if (!strncmp(cp, suspended, sizeof(suspended) - 1)) {
+ log_time real;
+ char *endp;
+ real.tv_sec = strtol(cp + sizeof(suspended) - 1, &endp, 10);
+ if (*endp == '.') {
+ real.tv_nsec = strtol(endp + 1, &endp, 10) * 1000000L;
+ if (reverse) {
+ correction -= real;
+ } else {
+ correction += real;
+ }
+ }
+ }
+
+ convertMonotonicToReal(now);
+ *buf = cp;
+ } else {
+ now = log_time(CLOCK_REALTIME);
+ }
+}
+
+// Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a
+// compensated start time.
+void LogKlog::synchronize(const char *buf) {
+ const char *cp = strstr(buf, "] PM: suspend e");
+ if (!cp) {
+ return;
+ }
+
+ do {
+ --cp;
+ } while ((cp > buf) && (isdigit(*cp) || isspace(*cp) || (*cp == '.')));
+
+ log_time now;
+ sniffTime(now, &cp, true);
+
+ char *suspended = strstr(buf, "] Suspended for ");
+ if (!suspended || (suspended > cp)) {
+ return;
+ }
+ cp = suspended;
+
+ do {
+ --cp;
+ } while ((cp > buf) && (isdigit(*cp) || isspace(*cp) || (*cp == '.')));
+
+ sniffTime(now, &cp, true);
+}
+
+// kernel log prefix, convert to a kernel log priority number
+static int parseKernelPrio(const char **buf) {
+ int pri = LOG_USER | LOG_INFO;
+ const char *cp = *buf;
+ if (*cp == '<') {
+ pri = 0;
+ while(isdigit(*++cp)) {
+ pri = (pri * 10) + *cp - '0';
+ }
+ if (*cp == '>') {
+ ++cp;
+ } else {
+ cp = *buf;
+ pri = LOG_USER | LOG_INFO;
+ }
+ *buf = cp;
+ }
+ return pri;
+}
+
+// Convert kernel log priority number into an Android Logger priority number
+static int convertKernelPrioToAndroidPrio(int pri) {
+ switch(pri & LOG_PRIMASK) {
+ case LOG_EMERG:
+ // FALLTHRU
+ case LOG_ALERT:
+ // FALLTHRU
+ case LOG_CRIT:
+ return ANDROID_LOG_FATAL;
+
+ case LOG_ERR:
+ return ANDROID_LOG_ERROR;
+
+ case LOG_WARNING:
+ return ANDROID_LOG_WARN;
+
+ default:
+ // FALLTHRU
+ case LOG_NOTICE:
+ // FALLTHRU
+ case LOG_INFO:
+ break;
+
+ case LOG_DEBUG:
+ return ANDROID_LOG_DEBUG;
+ }
+
+ return ANDROID_LOG_INFO;
+}
+
+//
+// log a message into the kernel log buffer
+//
+// Filter rules to parse <PRI> <TIME> <tag> and <message> in order for
+// them to appear correct in the logcat output:
+//
+// LOG_KERN (0):
+// <PRI>[<TIME>] <tag> ":" <message>
+// <PRI>[<TIME>] <tag> <tag> ":" <message>
+// <PRI>[<TIME>] <tag> <tag>_work ":" <message>
+// <PRI>[<TIME>] <tag> '<tag>.<num>' ":" <message>
+// <PRI>[<TIME>] <tag> '<tag><num>' ":" <message>
+// <PRI>[<TIME>] <tag>_host '<tag>.<num>' ":" <message>
+// (unimplemented) <PRI>[<TIME>] <tag> '<num>.<tag>' ":" <message>
+// <PRI>[<TIME>] "[INFO]"<tag> : <message>
+// <PRI>[<TIME>] "------------[ cut here ]------------" (?)
+// <PRI>[<TIME>] "---[ end trace 3225a3070ca3e4ac ]---" (?)
+// LOG_USER, LOG_MAIL, LOG_DAEMON, LOG_AUTH, LOG_SYSLOG, LOG_LPR, LOG_NEWS
+// LOG_UUCP, LOG_CRON, LOG_AUTHPRIV, LOG_FTP:
+// <PRI+TAG>[<TIME>] (see sys/syslog.h)
+// Observe:
+// Minimum tag length = 3 NB: drops things like r5:c00bbadf, but allow PM:
+// Maximum tag words = 2
+// Maximum tag length = 16 NB: we are thinking of how ugly logcat can get.
+// Not a Tag if there is no message content.
+// leading additional spaces means no tag, inherit last tag.
+// Not a Tag if <tag>: is "ERROR:", "WARNING:", "INFO:" or "CPU:"
+// Drop:
+// empty messages
+// messages with ' audit(' in them if auditd is running
+// logd.klogd:
+// return -1 if message logd.klogd: <signature>
+//
+int LogKlog::log(const char *buf) {
+ if (auditd && strstr(buf, " audit(")) {
+ return 0;
+ }
+
+ int pri = parseKernelPrio(&buf);
+
+ log_time now;
+ sniffTime(now, &buf, false);
+
+ // sniff for start marker
+ const char klogd_message[] = "logd.klogd: ";
+ if (!strncmp(buf, klogd_message, sizeof(klogd_message) - 1)) {
+ char *endp;
+ uint64_t sig = strtoll(buf + sizeof(klogd_message) - 1, &endp, 10);
+ if (sig == signature.nsec()) {
+ if (initialized) {
+ enableLogging = true;
+ } else {
+ enableLogging = false;
+ }
+ return -1;
+ }
+ return 0;
+ }
+
+ if (!enableLogging) {
+ return 0;
+ }
+
+ // Parse pid, tid and uid (not possible)
+ const pid_t pid = 0;
+ const pid_t tid = 0;
+ const uid_t uid = 0;
+
+ // Parse (rules at top) to pull out a tag from the incoming kernel message.
+ // Some may view the following as an ugly heuristic, the desire is to
+ // beautify the kernel logs into an Android Logging format; the goal is
+ // admirable but costly.
+ while (isspace(*buf)) {
+ ++buf;
+ }
+ if (!*buf) {
+ return 0;
+ }
+ const char *start = buf;
+ const char *tag = "";
+ const char *etag = tag;
+ if (!isspace(*buf)) {
+ const char *bt, *et, *cp;
+
+ bt = buf;
+ if (!strncmp(buf, "[INFO]", 6)) {
+ // <PRI>[<TIME>] "[INFO]"<tag> ":" message
+ bt = buf + 6;
+ }
+ for(et = bt; *et && (*et != ':') && !isspace(*et); ++et);
+ for(cp = et; isspace(*cp); ++cp);
+ size_t size;
+
+ if (*cp == ':') {
+ // One Word
+ tag = bt;
+ etag = et;
+ buf = cp + 1;
+ } else {
+ size = et - bt;
+ if (strncmp(bt, cp, size)) {
+ // <PRI>[<TIME>] <tag>_host '<tag>.<num>' : message
+ if (!strncmp(bt + size - 5, "_host", 5)
+ && !strncmp(bt, cp, size - 5)) {
+ const char *b = cp;
+ cp += size - 5;
+ if (*cp == '.') {
+ while (!isspace(*++cp) && (*cp != ':'));
+ const char *e;
+ for(e = cp; isspace(*cp); ++cp);
+ if (*cp == ':') {
+ tag = b;
+ etag = e;
+ buf = cp + 1;
+ }
+ }
+ } else {
+ while (!isspace(*++cp) && (*cp != ':'));
+ const char *e;
+ for(e = cp; isspace(*cp); ++cp);
+ // Two words
+ if (*cp == ':') {
+ tag = bt;
+ etag = e;
+ buf = cp + 1;
+ }
+ }
+ } else if (isspace(cp[size])) {
+ const char *b = cp;
+ cp += size;
+ while (isspace(*++cp));
+ // <PRI>[<TIME>] <tag> <tag> : message
+ if (*cp == ':') {
+ tag = bt;
+ etag = et;
+ buf = cp + 1;
+ }
+ } else if (cp[size] == ':') {
+ // <PRI>[<TIME>] <tag> <tag> : message
+ tag = bt;
+ etag = et;
+ buf = cp + size + 1;
+ } else if ((cp[size] == '.') || isdigit(cp[size])) {
+ // <PRI>[<TIME>] <tag> '<tag>.<num>' : message
+ // <PRI>[<TIME>] <tag> '<tag><num>' : message
+ const char *b = cp;
+ cp += size;
+ while (!isspace(*++cp) && (*cp != ':'));
+ const char *e = cp;
+ while (isspace(*cp)) {
+ ++cp;
+ }
+ if (*cp == ':') {
+ tag = b;
+ etag = e;
+ buf = cp + 1;
+ }
+ } else {
+ while (!isspace(*++cp) && (*cp != ':'));
+ const char *e = cp;
+ while (isspace(*cp)) {
+ ++cp;
+ }
+ // Two words
+ if (*cp == ':') {
+ tag = bt;
+ etag = e;
+ buf = cp + 1;
+ }
+ }
+ }
+ size = etag - tag;
+ if ((size <= 1)
+ || ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1])))
+ || ((size == 3) && !strncmp(tag, "CPU", 3))
+ || ((size == 7) && !strncmp(tag, "WARNING", 7))
+ || ((size == 5) && !strncmp(tag, "ERROR", 5))
+ || ((size == 4) && !strncmp(tag, "INFO", 4))) {
+ buf = start;
+ etag = tag = "";
+ }
+ }
+ size_t l = etag - tag;
+ while (isspace(*buf)) {
+ ++buf;
+ }
+ size_t n = 1 + l + 1 + strlen(buf) + 1;
+
+ // Allocate a buffer to hold the interpreted log message
+ int rc = n;
+ char *newstr = reinterpret_cast<char *>(malloc(n));
+ if (!newstr) {
+ rc = -ENOMEM;
+ return rc;
+ }
+ char *np = newstr;
+
+ // Convert priority into single-byte Android logger priority
+ *np = convertKernelPrioToAndroidPrio(pri);
+ ++np;
+
+ // Copy parsed tag following priority
+ strncpy(np, tag, l);
+ np += l;
+ *np = '\0';
+ ++np;
+
+ // Copy main message to the remainder
+ strcpy(np, buf);
+
+ // Log message
+ rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr,
+ (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX);
+ free(newstr);
+
+ // notify readers
+ if (!rc) {
+ reader->notifyNewLog();
+ }
+
+ return rc;
+}
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
new file mode 100644
index 0000000..8de9c87
--- /dev/null
+++ b/logd/LogKlog.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_KLOG_H__
+#define _LOGD_LOG_KLOG_H__
+
+#include <sysutils/SocketListener.h>
+#include <log/log_read.h>
+#include "LogReader.h"
+
+class LogKlog : public SocketListener {
+ LogBuffer *logbuf;
+ LogReader *reader;
+ const log_time signature;
+ const int fdWrite; // /dev/kmsg
+ const int fdRead; // /proc/kmsg
+ // Set once thread is started, separates KLOG_ACTION_READ_ALL
+ // and KLOG_ACTION_READ phases.
+ bool initialized;
+ // Used during each of the above phases to control logging.
+ bool enableLogging;
+ // set if we are also running auditd, to filter out audit reports from
+ // our copy of the kernel log
+ bool auditd;
+
+ static log_time correction;
+
+public:
+ LogKlog(LogBuffer *buf, LogReader *reader, int fdWrite, int fdRead, bool auditd);
+ int log(const char *buf);
+ void synchronize(const char *buf);
+
+ static void convertMonotonicToReal(log_time &real) { real += correction; }
+
+protected:
+ void sniffTime(log_time &now, const char **buf, bool reverse);
+ void calculateCorrection(const log_time &monotonic, const char *real_string);
+ virtual bool onDataAvailable(SocketClient *cli);
+
+};
+
+#endif
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index fc9e30f..b29f5ab 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -23,18 +23,23 @@
#include <cutils/sockets.h>
#include <log/logger.h>
+#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#include "LogListener.h"
-LogListener::LogListener(LogBuffer *buf, LogReader *reader)
- : SocketListener(getLogSocket(), false)
- , logbuf(buf)
- , reader(reader)
-{ }
+LogListener::LogListener(LogBuffer *buf, LogReader *reader) :
+ SocketListener(getLogSocket(), false),
+ logbuf(buf),
+ reader(reader) {
+}
bool LogListener::onDataAvailable(SocketClient *cli) {
- prctl(PR_SET_NAME, "logd.writer");
+ static bool name_set;
+ if (!name_set) {
+ prctl(PR_SET_NAME, "logd.writer");
+ name_set = true;
+ }
char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time)
+ LOGGER_ENTRY_MAX_PAYLOAD];
@@ -75,7 +80,7 @@
return false;
}
- if (cred->uid == getuid()) {
+ if (cred->uid == AID_LOGD) {
// ignore log messages we send to ourself.
// Such log messages are often generated by libraries we depend on
// which use standard Android logging.
@@ -83,7 +88,7 @@
}
android_log_header_t *header = reinterpret_cast<android_log_header_t *>(buffer);
- if (/* header->id < LOG_ID_MIN || */ header->id >= LOG_ID_MAX) {
+ if (/* header->id < LOG_ID_MIN || */ header->id >= LOG_ID_MAX || header->id == LOG_ID_KERNEL) {
return false;
}
@@ -93,10 +98,11 @@
// NB: hdr.msg_flags & MSG_TRUNC is not tested, silently passing a
// truncated message to the logs.
- logbuf->log((log_id_t)header->id, header->realtime,
- cred->uid, cred->pid, header->tid, msg,
- ((size_t) n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX);
- reader->notifyNewLog();
+ if (logbuf->log((log_id_t)header->id, header->realtime,
+ cred->uid, cred->pid, header->tid, msg,
+ ((size_t) n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX) >= 0) {
+ reader->notifyNewLog();
+ }
return true;
}
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index f7df275..c7deec0 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -24,10 +24,10 @@
#include "LogReader.h"
#include "FlushCommand.h"
-LogReader::LogReader(LogBuffer *logbuf)
- : SocketListener(getLogSocket(), true)
- , mLogbuf(*logbuf)
-{ }
+LogReader::LogReader(LogBuffer *logbuf) :
+ SocketListener(getLogSocket(), true),
+ mLogbuf(*logbuf) {
+}
// When we are notified a new log entry is available, inform
// all of our listening sockets.
@@ -37,7 +37,11 @@
}
bool LogReader::onDataAvailable(SocketClient *cli) {
- prctl(PR_SET_NAME, "logd.reader");
+ static bool name_set;
+ if (!name_set) {
+ prctl(PR_SET_NAME, "logd.reader");
+ name_set = true;
+ }
char buffer[255];
@@ -112,14 +116,14 @@
uint64_t last;
public:
- LogFindStart(unsigned logMask, pid_t pid, log_time &start, uint64_t &sequence)
- : mPid(pid)
- , mLogMask(logMask)
- , startTimeSet(false)
- , start(start)
- , sequence(sequence)
- , last(sequence)
- { }
+ LogFindStart(unsigned logMask, pid_t pid, log_time &start, uint64_t &sequence) :
+ mPid(pid),
+ mLogMask(logMask),
+ startTimeSet(false),
+ start(start),
+ sequence(sequence),
+ last(sequence) {
+ }
static int callback(const LogBufferElement *element, void *obj) {
LogFindStart *me = reinterpret_cast<LogFindStart *>(obj);
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 5a70689..90c49c0 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -14,10 +14,11 @@
* limitations under the License.
*/
+#include <algorithm> // std::max
#include <fcntl.h>
-#include <malloc.h>
-#include <stdarg.h>
-#include <time.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
#include <log/logger.h>
#include <private/android_filesystem_config.h>
@@ -25,80 +26,23 @@
#include "LogStatistics.h"
-PidStatistics::PidStatistics(pid_t pid, char *name)
- : pid(pid)
- , mSizesTotal(0)
- , mElementsTotal(0)
- , mSizes(0)
- , mElements(0)
- , name(name)
- , mGone(false)
-{ }
-
-#ifdef DO_NOT_ERROR_IF_PIDSTATISTICS_USES_A_COPY_CONSTRUCTOR
-PidStatistics::PidStatistics(const PidStatistics ©)
- : pid(copy->pid)
- , name(copy->name ? strdup(copy->name) : NULL)
- , mSizesTotal(copy->mSizesTotal)
- , mElementsTotal(copy->mElementsTotal)
- , mSizes(copy->mSizes)
- , mElements(copy->mElements)
- , mGone(copy->mGone)
-{ }
-#endif
-
-PidStatistics::~PidStatistics() {
- free(name);
-}
-
-bool PidStatistics::pidGone() {
- if (mGone || (pid == gone)) {
- return true;
- }
- if (pid == 0) {
- return false;
- }
- if (kill(pid, 0) && (errno != EPERM)) {
- mGone = true;
- return true;
- }
- return false;
-}
-
-void PidStatistics::setName(char *new_name) {
- free(name);
- name = new_name;
-}
-
-void PidStatistics::add(unsigned short size) {
- mSizesTotal += size;
- ++mElementsTotal;
- mSizes += size;
- ++mElements;
-}
-
-bool PidStatistics::subtract(unsigned short size) {
- mSizes -= size;
- --mElements;
- return (mElements == 0) && pidGone();
-}
-
-void PidStatistics::addTotal(size_t size, size_t element) {
- if (pid == gone) {
- mSizesTotal += size;
- mElementsTotal += element;
+LogStatistics::LogStatistics() : enable(false) {
+ log_id_for_each(id) {
+ mSizes[id] = 0;
+ mElements[id] = 0;
+ mSizesTotal[id] = 0;
+ mElementsTotal[id] = 0;
}
}
-// must call free to release return value
-// If only we could sniff our own logs for:
-// <time> <pid> <pid> E AndroidRuntime: Process: <name>, PID: <pid>
-// which debuggerd prints as a process is crashing.
-char *PidStatistics::pidToName(pid_t pid) {
+namespace android {
+
+// caller must own and free character string
+char *pidToName(pid_t pid) {
char *retval = NULL;
- if (pid == 0) { // special case from auditd for kernel
- retval = strdup("logd.auditd");
- } else if (pid != gone) {
+ if (pid == 0) { // special case from auditd/klogd for kernel
+ retval = strdup("logd");
+ } else {
char buffer[512];
snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid);
int fd = open(buffer, O_RDONLY);
@@ -117,358 +61,149 @@
return retval;
}
-UidStatistics::UidStatistics(uid_t uid)
- : uid(uid)
- , mSizes(0)
- , mElements(0) {
- Pids.clear();
}
-UidStatistics::~UidStatistics() {
- PidStatisticsCollection::iterator it;
- for (it = begin(); it != end();) {
- delete (*it);
- it = erase(it);
- }
-}
-
-void UidStatistics::add(unsigned short size, pid_t pid) {
- mSizes += size;
- ++mElements;
-
- PidStatistics *p = NULL;
- PidStatisticsCollection::iterator last;
- PidStatisticsCollection::iterator it;
- for (last = it = begin(); it != end(); last = it, ++it) {
- p = *it;
- if (pid == p->getPid()) {
- p->add(size);
- return;
- }
- }
- // insert if the gone entry.
- bool insert_before_last = (last != it) && p && (p->getPid() == p->gone);
- p = new PidStatistics(pid, pidToName(pid));
- if (insert_before_last) {
- insert(last, p);
- } else {
- push_back(p);
- }
- p->add(size);
-}
-
-void UidStatistics::subtract(unsigned short size, pid_t pid) {
- mSizes -= size;
- --mElements;
-
- PidStatisticsCollection::iterator it;
- for (it = begin(); it != end(); ++it) {
- PidStatistics *p = *it;
- if (pid == p->getPid()) {
- if (p->subtract(size)) {
- size_t szsTotal = p->sizesTotal();
- size_t elsTotal = p->elementsTotal();
- delete p;
- erase(it);
- it = end();
- --it;
- if (it == end()) {
- p = new PidStatistics(p->gone);
- push_back(p);
- } else {
- p = *it;
- if (p->getPid() != p->gone) {
- p = new PidStatistics(p->gone);
- push_back(p);
- }
- }
- p->addTotal(szsTotal, elsTotal);
- }
- return;
- }
- }
-}
-
-void UidStatistics::sort() {
- for (bool pass = true; pass;) {
- pass = false;
- PidStatisticsCollection::iterator it = begin();
- if (it != end()) {
- PidStatisticsCollection::iterator lt = it;
- PidStatistics *l = (*lt);
- while (++it != end()) {
- PidStatistics *n = (*it);
- if ((n->getPid() != n->gone) && (n->sizes() > l->sizes())) {
- pass = true;
- erase(it);
- insert(lt, n);
- it = lt;
- n = l;
- }
- lt = it;
- l = n;
- }
- }
- }
-}
-
-size_t UidStatistics::sizes(pid_t pid) {
- if (pid == pid_all) {
- return sizes();
- }
-
- PidStatisticsCollection::iterator it;
- for (it = begin(); it != end(); ++it) {
- PidStatistics *p = *it;
- if (pid == p->getPid()) {
- return p->sizes();
- }
- }
- return 0;
-}
-
-size_t UidStatistics::elements(pid_t pid) {
- if (pid == pid_all) {
- return elements();
- }
-
- PidStatisticsCollection::iterator it;
- for (it = begin(); it != end(); ++it) {
- PidStatistics *p = *it;
- if (pid == p->getPid()) {
- return p->elements();
- }
- }
- return 0;
-}
-
-size_t UidStatistics::sizesTotal(pid_t pid) {
- size_t sizes = 0;
- PidStatisticsCollection::iterator it;
- for (it = begin(); it != end(); ++it) {
- PidStatistics *p = *it;
- if ((pid == pid_all) || (pid == p->getPid())) {
- sizes += p->sizesTotal();
- }
- }
- return sizes;
-}
-
-size_t UidStatistics::elementsTotal(pid_t pid) {
- size_t elements = 0;
- PidStatisticsCollection::iterator it;
- for (it = begin(); it != end(); ++it) {
- PidStatistics *p = *it;
- if ((pid == pid_all) || (pid == p->getPid())) {
- elements += p->elementsTotal();
- }
- }
- return elements;
-}
-
-LidStatistics::LidStatistics() {
- Uids.clear();
-}
-
-LidStatistics::~LidStatistics() {
- UidStatisticsCollection::iterator it;
- for (it = begin(); it != end();) {
- delete (*it);
- it = Uids.erase(it);
- }
-}
-
-void LidStatistics::add(unsigned short size, uid_t uid, pid_t pid) {
- UidStatistics *u;
- UidStatisticsCollection::iterator it;
- UidStatisticsCollection::iterator last;
-
- if (uid == (uid_t) -1) { // init
- uid = (uid_t) AID_ROOT;
- }
-
- for (last = it = begin(); it != end(); last = it, ++it) {
- u = *it;
- if (uid == u->getUid()) {
- u->add(size, pid);
- if ((last != it) && ((*last)->sizesTotal() < u->sizesTotal())) {
- Uids.erase(it);
- Uids.insert(last, u);
- }
- return;
- }
- }
- u = new UidStatistics(uid);
- if ((last != it) && ((*last)->sizesTotal() < (size_t) size)) {
- Uids.insert(last, u);
- } else {
- Uids.push_back(u);
- }
- u->add(size, pid);
-}
-
-void LidStatistics::subtract(unsigned short size, uid_t uid, pid_t pid) {
- if (uid == (uid_t) -1) { // init
- uid = (uid_t) AID_ROOT;
- }
-
- UidStatisticsCollection::iterator it;
- for (it = begin(); it != end(); ++it) {
- UidStatistics *u = *it;
- if (uid == u->getUid()) {
- u->subtract(size, pid);
- return;
- }
- }
-}
-
-void LidStatistics::sort() {
- for (bool pass = true; pass;) {
- pass = false;
- UidStatisticsCollection::iterator it = begin();
- if (it != end()) {
- UidStatisticsCollection::iterator lt = it;
- UidStatistics *l = (*lt);
- while (++it != end()) {
- UidStatistics *n = (*it);
- if (n->sizes() > l->sizes()) {
- pass = true;
- Uids.erase(it);
- Uids.insert(lt, n);
- it = lt;
- n = l;
- }
- lt = it;
- l = n;
- }
- }
- }
-}
-
-size_t LidStatistics::sizes(uid_t uid, pid_t pid) {
- size_t sizes = 0;
- UidStatisticsCollection::iterator it;
- for (it = begin(); it != end(); ++it) {
- UidStatistics *u = *it;
- if ((uid == uid_all) || (uid == u->getUid())) {
- sizes += u->sizes(pid);
- }
- }
- return sizes;
-}
-
-size_t LidStatistics::elements(uid_t uid, pid_t pid) {
- size_t elements = 0;
- UidStatisticsCollection::iterator it;
- for (it = begin(); it != end(); ++it) {
- UidStatistics *u = *it;
- if ((uid == uid_all) || (uid == u->getUid())) {
- elements += u->elements(pid);
- }
- }
- return elements;
-}
-
-size_t LidStatistics::sizesTotal(uid_t uid, pid_t pid) {
- size_t sizes = 0;
- UidStatisticsCollection::iterator it;
- for (it = begin(); it != end(); ++it) {
- UidStatistics *u = *it;
- if ((uid == uid_all) || (uid == u->getUid())) {
- sizes += u->sizesTotal(pid);
- }
- }
- return sizes;
-}
-
-size_t LidStatistics::elementsTotal(uid_t uid, pid_t pid) {
- size_t elements = 0;
- UidStatisticsCollection::iterator it;
- for (it = begin(); it != end(); ++it) {
- UidStatistics *u = *it;
- if ((uid == uid_all) || (uid == u->getUid())) {
- elements += u->elementsTotal(pid);
- }
- }
- return elements;
-}
-
-LogStatistics::LogStatistics()
- : mStatistics(false)
- , start(CLOCK_MONOTONIC) {
- log_id_for_each(i) {
- mSizes[i] = 0;
- mElements[i] = 0;
- }
-}
-
-void LogStatistics::add(unsigned short size,
- log_id_t log_id, uid_t uid, pid_t pid) {
+void LogStatistics::add(LogBufferElement *e) {
+ log_id_t log_id = e->getLogId();
+ unsigned short size = e->getMsgLen();
mSizes[log_id] += size;
++mElements[log_id];
- if (!mStatistics) {
+
+ mSizesTotal[log_id] += size;
+ ++mElementsTotal[log_id];
+
+ if (log_id == LOG_ID_KERNEL) {
return;
}
- id(log_id).add(size, uid, pid);
+
+ uidTable[log_id].add(e->getUid(), e);
+
+ if (!enable) {
+ return;
+ }
+
+ pidTable.add(e->getPid(), e);
+ tidTable.add(e->getTid(), e);
+
+ uint32_t tag = e->getTag();
+ if (tag) {
+ tagTable.add(tag, e);
+ }
}
-void LogStatistics::subtract(unsigned short size,
- log_id_t log_id, uid_t uid, pid_t pid) {
+void LogStatistics::subtract(LogBufferElement *e) {
+ log_id_t log_id = e->getLogId();
+ unsigned short size = e->getMsgLen();
mSizes[log_id] -= size;
--mElements[log_id];
- if (!mStatistics) {
+
+ if (log_id == LOG_ID_KERNEL) {
return;
}
- id(log_id).subtract(size, uid, pid);
+
+ uidTable[log_id].subtract(e->getUid(), e);
+
+ if (!enable) {
+ return;
+ }
+
+ pidTable.subtract(e->getPid(), e);
+ tidTable.subtract(e->getTid(), e);
+
+ uint32_t tag = e->getTag();
+ if (tag) {
+ tagTable.subtract(tag, e);
+ }
}
-size_t LogStatistics::sizes(log_id_t log_id, uid_t uid, pid_t pid) {
- if (log_id != log_id_all) {
- return id(log_id).sizes(uid, pid);
+// 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();
+ mSizes[log_id] -= size;
+
+ uidTable[log_id].drop(e->getUid(), e);
+
+ if (!enable) {
+ return;
}
- size_t sizes = 0;
- log_id_for_each(i) {
- sizes += id(i).sizes(uid, pid);
- }
- return sizes;
+
+ pidTable.drop(e->getPid(), e);
+ tidTable.drop(e->getTid(), e);
}
-size_t LogStatistics::elements(log_id_t log_id, uid_t uid, pid_t pid) {
- if (log_id != log_id_all) {
- return id(log_id).elements(uid, pid);
+// caller must own and free character string
+char *LogStatistics::uidToName(uid_t uid) {
+ // Local hard coded favourites
+ if (uid == AID_LOGD) {
+ return strdup("auditd");
}
- size_t elements = 0;
- log_id_for_each(i) {
- elements += id(i).elements(uid, pid);
+
+ // Android hard coded
+ const struct android_id_info *info = android_ids;
+
+ for (size_t i = 0; i < android_id_count; ++i) {
+ if (info->aid == uid) {
+ return strdup(info->name);
+ }
+ ++info;
}
- return elements;
+
+ // Parse /data/system/packages.list
+ uid_t userId = uid % AID_USER;
+ char *name = android::uidToName(userId);
+ if (!name && (userId > (AID_SHARED_GID_START - AID_APP))) {
+ name = android::uidToName(userId - (AID_SHARED_GID_START - AID_APP));
+ }
+ if (name) {
+ return name;
+ }
+
+ // report uid -> pid(s) -> pidToName if unique
+ ssize_t index = -1;
+ while ((index = pidTable.next(index)) != -1) {
+ const PidEntry &entry = pidTable.entryAt(index);
+
+ if (entry.getUid() == uid) {
+ const char *n = entry.getName();
+
+ if (n) {
+ if (!name) {
+ name = strdup(n);
+ } else if (strcmp(name, n)) {
+ free(name);
+ name = NULL;
+ break;
+ }
+ }
+ }
+ }
+
+ // No one
+ return name;
}
-size_t LogStatistics::sizesTotal(log_id_t log_id, uid_t uid, pid_t pid) {
- if (log_id != log_id_all) {
- return id(log_id).sizesTotal(uid, pid);
+static void format_line(android::String8 &output,
+ android::String8 &name, android::String8 &size, android::String8 &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()) {
+ output.appendFormat("%s%*s%*s\n", name.string(),
+ (int)size_len, size.string(),
+ (int)drop_len, pruned.string());
+ } else {
+ output.appendFormat("%s%*s\n", name.string(),
+ (int)size_len, size.string());
}
- size_t sizes = 0;
- log_id_for_each(i) {
- sizes += id(i).sizesTotal(uid, pid);
- }
- return sizes;
}
-size_t LogStatistics::elementsTotal(log_id_t log_id, uid_t uid, pid_t pid) {
- if (log_id != log_id_all) {
- return id(log_id).elementsTotal(uid, pid);
- }
- size_t elements = 0;
- log_id_for_each(i) {
- elements += id(i).elementsTotal(uid, pid);
- }
- return elements;
-}
-
-void LogStatistics::format(char **buf,
- uid_t uid, unsigned int logMask, log_time oldest) {
- static const unsigned short spaces_current = 13;
+void LogStatistics::format(char **buf, uid_t uid, unsigned int logMask) {
static const unsigned short spaces_total = 19;
if (*buf) {
@@ -476,368 +211,323 @@
*buf = NULL;
}
- android::String8 string(" span -> size/num");
- size_t oldLength;
- short spaces = 2;
+ // Report on total logging, current and for all time
- log_id_for_each(i) {
- if (!(logMask & (1 << i))) {
+ android::String8 output("size/num");
+ size_t oldLength;
+ short spaces = 1;
+
+ log_id_for_each(id) {
+ if (!(logMask & (1 << id))) {
continue;
}
- oldLength = string.length();
+ oldLength = output.length();
if (spaces < 0) {
spaces = 0;
}
- string.appendFormat("%*s%s", spaces, "", android_log_id_to_name(i));
- spaces += spaces_total + oldLength - string.length();
-
- LidStatistics &l = id(i);
- l.sort();
-
- UidStatisticsCollection::iterator iu;
- for (iu = l.begin(); iu != l.end(); ++iu) {
- (*iu)->sort();
- }
+ output.appendFormat("%*s%s", spaces, "", android_log_id_to_name(id));
+ spaces += spaces_total + oldLength - output.length();
}
- spaces = 1;
- log_time t(CLOCK_MONOTONIC);
- unsigned long long d;
- if (mStatistics) {
- d = t.nsec() - start.nsec();
- string.appendFormat("\nTotal%4llu:%02llu:%02llu.%09llu",
- d / NS_PER_SEC / 60 / 60, (d / NS_PER_SEC / 60) % 60,
- (d / NS_PER_SEC) % 60, d % NS_PER_SEC);
+ spaces = 4;
+ output.appendFormat("\nTotal");
- log_id_for_each(i) {
- if (!(logMask & (1 << i))) {
- continue;
- }
- oldLength = string.length();
- if (spaces < 0) {
- spaces = 0;
- }
- string.appendFormat("%*s%zu/%zu", spaces, "",
- sizesTotal(i), elementsTotal(i));
- spaces += spaces_total + oldLength - string.length();
+ log_id_for_each(id) {
+ if (!(logMask & (1 << id))) {
+ continue;
}
- spaces = 1;
+ oldLength = output.length();
+ if (spaces < 0) {
+ spaces = 0;
+ }
+ output.appendFormat("%*s%zu/%zu", spaces, "",
+ sizesTotal(id), elementsTotal(id));
+ spaces += spaces_total + oldLength - output.length();
}
- d = t.nsec() - oldest.nsec();
- string.appendFormat("\nNow%6llu:%02llu:%02llu.%09llu",
- d / NS_PER_SEC / 60 / 60, (d / NS_PER_SEC / 60) % 60,
- (d / NS_PER_SEC) % 60, d % NS_PER_SEC);
+ spaces = 6;
+ output.appendFormat("\nNow");
- log_id_for_each(i) {
- if (!(logMask & (1 << i))) {
+ log_id_for_each(id) {
+ if (!(logMask & (1 << id))) {
continue;
}
- size_t els = elements(i);
+ size_t els = elements(id);
if (els) {
- oldLength = string.length();
+ oldLength = output.length();
if (spaces < 0) {
spaces = 0;
}
- string.appendFormat("%*s%zu/%zu", spaces, "", sizes(i), els);
- spaces -= string.length() - oldLength;
+ output.appendFormat("%*s%zu/%zu", spaces, "", sizes(id), els);
+ spaces -= output.length() - oldLength;
}
spaces += spaces_total;
}
- // Construct list of worst spammers by Pid
- static const unsigned char num_spammers = 10;
- bool header = false;
+ // Report on Chattiest
- log_id_for_each(i) {
- if (!(logMask & (1 << i))) {
+ // Chattiest by application (UID)
+ static const size_t maximum_sorted_entries = 32;
+ log_id_for_each(id) {
+ if (!(logMask & (1 << id))) {
continue;
}
- PidStatisticsCollection pids;
- pids.clear();
-
- LidStatistics &l = id(i);
- UidStatisticsCollection::iterator iu;
- for (iu = l.begin(); iu != l.end(); ++iu) {
- UidStatistics &u = *(*iu);
- PidStatisticsCollection::iterator ip;
- for (ip = u.begin(); ip != u.end(); ++ip) {
- PidStatistics *p = (*ip);
- if (p->getPid() == p->gone) {
- break;
- }
-
- size_t mySizes = p->sizes();
-
- PidStatisticsCollection::iterator q;
- unsigned char num = 0;
- for (q = pids.begin(); q != pids.end(); ++q) {
- if (mySizes > (*q)->sizes()) {
- pids.insert(q, p);
- break;
- }
- // do we need to traverse deeper in the list?
- if (++num > num_spammers) {
- break;
- }
- }
- if (q == pids.end()) {
- pids.push_back(p);
- }
- }
- }
-
- size_t threshold = sizes(i);
- if (threshold < 65536) {
- threshold = 65536;
- }
- threshold /= 100;
-
- PidStatisticsCollection::iterator pt = pids.begin();
-
- for(int line = 0;
- (pt != pids.end()) && (line < num_spammers);
- ++line, pt = pids.erase(pt)) {
- PidStatistics *p = *pt;
-
- size_t sizes = p->sizes();
- if (sizes < threshold) {
- break;
- }
-
- char *name = p->getName();
- pid_t pid = p->getPid();
- if (!name || !*name) {
- name = pidToName(pid);
- if (name) {
- if (*name) {
- p->setName(name);
- } else {
- free(name);
- name = NULL;
- }
- }
- }
-
- if (!header) {
- string.appendFormat("\n\nChattiest clients:\n"
- "log id %-*s PID[?] name",
- spaces_total, "size/total");
- header = true;
- }
-
- size_t sizesTotal = p->sizesTotal();
-
- android::String8 sz("");
- if (sizes == sizesTotal) {
- sz.appendFormat("%zu", sizes);
- } else {
- sz.appendFormat("%zu/%zu", sizes, sizesTotal);
- }
-
- android::String8 pd("");
- pd.appendFormat("%u%c", pid, p->pidGone() ? '?' : ' ');
-
- string.appendFormat("\n%-7s%-*s %-7s%s",
- line ? "" : android_log_id_to_name(i),
- spaces_total, sz.string(), pd.string(),
- name ? name : "");
- }
-
- pids.clear();
- }
-
- log_id_for_each(i) {
- if (!(logMask & (1 << i))) {
- continue;
- }
-
- header = false;
- bool first = true;
-
- UidStatisticsCollection::iterator ut;
- for(ut = id(i).begin(); ut != id(i).end(); ++ut) {
- UidStatistics *up = *ut;
- if ((uid != AID_ROOT) && (uid != up->getUid())) {
+ 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;
}
- PidStatisticsCollection::iterator pt = up->begin();
- if (pt == up->end()) {
- continue;
- }
-
- android::String8 intermediate;
-
- if (!header) {
- // header below tuned to match spaces_total and spaces_current
- spaces = 0;
- intermediate = string.format("%s: UID/PID Total size/num",
- android_log_id_to_name(i));
- string.appendFormat("\n\n%-31sNow "
- "UID/PID[?] Total Now",
- intermediate.string());
- intermediate.clear();
- header = true;
- }
-
- bool oneline = ++pt == up->end();
- --pt;
-
- if (!oneline) {
- first = true;
- } else if (!first && (spaces > 0)) {
- string.appendFormat("%*s", spaces, "");
- }
- spaces = 0;
-
- uid_t u = up->getUid();
- PidStatistics *pp = *pt;
- pid_t p = pp->getPid();
-
- if (!oneline) {
- intermediate = string.format("%d", u);
- } else if (p == PidStatistics::gone) {
- intermediate = string.format("%d/?", u);
- } else if (pp->pidGone()) {
- intermediate = string.format("%d/%d?", u, p);
- } else {
- intermediate = string.format("%d/%d", u, p);
- }
- string.appendFormat(first ? "\n%-12s" : "%-12s",
- intermediate.string());
- intermediate.clear();
-
- size_t elsTotal = up->elementsTotal();
- oldLength = string.length();
- string.appendFormat("%zu/%zu", up->sizesTotal(), elsTotal);
- spaces += spaces_total + oldLength - string.length();
-
- size_t els = up->elements();
- if (els == elsTotal) {
- if (spaces < 0) {
- spaces = 0;
+ if (!headerPrinted) {
+ output.appendFormat("\n\n");
+ android::String8 name("");
+ if (uid == AID_ROOT) {
+ name.appendFormat(
+ "Chattiest UIDs in %s log buffer:",
+ android_log_id_to_name(id));
+ } else {
+ name.appendFormat(
+ "Logging for your UID in %s log buffer:",
+ android_log_id_to_name(id));
}
- string.appendFormat("%*s=", spaces, "");
- spaces = -1;
- } else if (els) {
- oldLength = string.length();
- if (spaces < 0) {
- spaces = 0;
+ android::String8 size("Size");
+ android::String8 pruned("Pruned");
+ if (!worstUidEnabledForLogid(id)) {
+ pruned.setTo("");
}
- string.appendFormat("%*s%zu/%zu", spaces, "", up->sizes(), els);
- spaces -= string.length() - oldLength;
- }
- spaces += spaces_current;
+ format_line(output, name, size, pruned);
- first = !first;
+ name.setTo("UID PACKAGE");
+ size.setTo("BYTES");
+ pruned.setTo("LINES");
+ if (!worstUidEnabledForLogid(id)) {
+ pruned.setTo("");
+ }
+ format_line(output, name, size, pruned);
- if (oneline) {
- continue;
+ headerPrinted = true;
}
- size_t gone_szs = 0;
- size_t gone_els = 0;
-
- for(; pt != up->end(); ++pt) {
- pp = *pt;
- p = pp->getPid();
-
- // If a PID no longer has any current logs, and is not
- // active anymore, skip & report totals for gone.
- elsTotal = pp->elementsTotal();
- size_t szsTotal = pp->sizesTotal();
- if (p == pp->gone) {
- gone_szs += szsTotal;
- gone_els += elsTotal;
- continue;
- }
- els = pp->elements();
- bool gone = pp->pidGone();
- if (gone && (els == 0)) {
- // ToDo: garbage collection: move this statistical bucket
- // from its current UID/PID to UID/? (races and
- // wrap around are our achilles heel). Below is
- // merely lipservice to catch PIDs that were still
- // around when the stats were pruned to zero.
- gone_szs += szsTotal;
- gone_els += elsTotal;
- continue;
- }
-
- if (!first && (spaces > 0)) {
- string.appendFormat("%*s", spaces, "");
- }
- spaces = 0;
-
- intermediate = string.format(gone ? "%d/%d?" : "%d/%d", u, p);
- string.appendFormat(first ? "\n%-12s" : "%-12s",
- intermediate.string());
- intermediate.clear();
-
- oldLength = string.length();
- string.appendFormat("%zu/%zu", szsTotal, elsTotal);
- spaces += spaces_total + oldLength - string.length();
-
- if (els == elsTotal) {
- if (spaces < 0) {
- spaces = 0;
- }
- string.appendFormat("%*s=", spaces, "");
- spaces = -1;
- } else if (els) {
- oldLength = string.length();
- if (spaces < 0) {
- spaces = 0;
- }
- string.appendFormat("%*s%zu/%zu", spaces, "",
- pp->sizes(), els);
- spaces -= string.length() - oldLength;
- }
- spaces += spaces_current;
-
- first = !first;
+ android::String8 name("");
+ name.appendFormat("%u", u);
+ char *n = uidToName(u);
+ if (n) {
+ name.appendFormat("%*s%s", (int)std::max(6 - name.length(), (size_t)1), "", n);
+ free(n);
}
- if (gone_els) {
- if (!first && (spaces > 0)) {
- string.appendFormat("%*s", spaces, "");
- }
+ android::String8 size("");
+ size.appendFormat("%zu", entry->getSizes());
- intermediate = string.format("%d/?", u);
- string.appendFormat(first ? "\n%-12s" : "%-12s",
- intermediate.string());
- intermediate.clear();
-
- spaces = spaces_total + spaces_current;
-
- oldLength = string.length();
- string.appendFormat("%zu/%zu", gone_szs, gone_els);
- spaces -= string.length() - oldLength;
-
- first = !first;
+ android::String8 pruned("");
+ size_t dropped = entry->getDropped();
+ if (dropped) {
+ pruned.appendFormat("%zu", dropped);
}
+
+ format_line(output, name, size, pruned);
}
}
- *buf = strdup(string.string());
+ 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.appendFormat("\n\n");
+ android::String8 name("");
+ if (uid == AID_ROOT) {
+ name.appendFormat("Chattiest PIDs:");
+ } else {
+ name.appendFormat("Logging for this PID:");
+ }
+ android::String8 size("Size");
+ android::String8 pruned("Pruned");
+ format_line(output, name, size, pruned);
+
+ name.setTo(" PID/UID COMMAND LINE");
+ size.setTo("BYTES");
+ pruned.setTo("LINES");
+ format_line(output, name, size, pruned);
+
+ headerPrinted = true;
+ }
+
+ android::String8 name("");
+ name.appendFormat("%5u/%u", entry->getKey(), u);
+ const char *n = entry->getName();
+ if (n) {
+ name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", n);
+ } else {
+ char *un = uidToName(u);
+ if (un) {
+ name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", un);
+ free(un);
+ }
+ }
+
+ android::String8 size("");
+ size.appendFormat("%zu", entry->getSizes());
+
+ android::String8 pruned("");
+ size_t dropped = entry->getDropped();
+ if (dropped) {
+ pruned.appendFormat("%zu", dropped);
+ }
+
+ format_line(output, 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.appendFormat("\n\n");
+ android::String8 name("Chattiest TIDs:");
+ android::String8 size("Size");
+ android::String8 pruned("Pruned");
+ format_line(output, name, size, pruned);
+
+ name.setTo(" TID/UID COMM");
+ size.setTo("BYTES");
+ pruned.setTo("LINES");
+ format_line(output, name, size, pruned);
+
+ headerPrinted = true;
+ }
+
+ android::String8 name("");
+ name.appendFormat("%5u/%u", entry->getKey(), u);
+ const char *n = entry->getName();
+ if (n) {
+ name.appendFormat("%*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.appendFormat("%*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.
+ }
+
+ android::String8 size("");
+ size.appendFormat("%zu", entry->getSizes());
+
+ android::String8 pruned("");
+ size_t dropped = entry->getDropped();
+ if (dropped) {
+ pruned.appendFormat("%zu", dropped);
+ }
+
+ format_line(output, name, size, pruned);
+ }
+ }
+
+ 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;
+ }
+
+ android::String8 pruned("");
+
+ if (!headerPrinted) {
+ output.appendFormat("\n\n");
+ android::String8 name("Chattiest events log buffer TAGs:");
+ android::String8 size("Size");
+ format_line(output, name, size, pruned);
+
+ name.setTo(" TAG/UID TAGNAME");
+ size.setTo("BYTES");
+ format_line(output, name, size, pruned);
+
+ headerPrinted = true;
+ }
+
+ android::String8 name("");
+ if (u == (uid_t)-1) {
+ name.appendFormat("%7u", entry->getKey());
+ } else {
+ name.appendFormat("%7u/%u", entry->getKey(), u);
+ }
+ const char *n = entry->getName();
+ if (n) {
+ name.appendFormat("%*s%s", (int)std::max(14 - name.length(), (size_t)1), "", n);
+ }
+
+ android::String8 size("");
+ size.appendFormat("%zu", entry->getSizes());
+
+ format_line(output, name, size, pruned);
+ }
+ }
+
+ *buf = strdup(output.string());
+}
+
+namespace android {
+
+uid_t pidToUid(pid_t pid) {
+ char buffer[512];
+ snprintf(buffer, sizeof(buffer), "/proc/%u/status", pid);
+ FILE *fp = fopen(buffer, "r");
+ if (fp) {
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ int uid;
+ if (sscanf(buffer, "Uid: %d", &uid) == 1) {
+ fclose(fp);
+ return uid;
+ }
+ }
+ fclose(fp);
+ }
+ return AID_LOGD; // associate this with the logger
+}
+
}
uid_t LogStatistics::pidToUid(pid_t pid) {
- log_id_for_each(i) {
- LidStatistics &l = id(i);
- UidStatisticsCollection::iterator iu;
- for (iu = l.begin(); iu != l.end(); ++iu) {
- UidStatistics &u = *(*iu);
- PidStatisticsCollection::iterator ip;
- for (ip = u.begin(); ip != u.end(); ++ip) {
- if ((*ip)->getPid() == pid) {
- return u.getUid();
- }
- }
- }
+ return pidTable.entryAt(pidTable.add(pid)).getUid();
+}
+
+// caller must free character string
+char *LogStatistics::pidToName(pid_t pid) {
+ const char *name = pidTable.entryAt(pidTable.add(pid)).getName();
+ if (!name) {
+ return NULL;
}
- return getuid(); // associate this with the logger
+ return strdup(name);
}
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index f892cd0..f60f3ed 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -17,178 +17,323 @@
#ifndef _LOGD_LOG_STATISTICS_H__
#define _LOGD_LOG_STATISTICS_H__
+#include <memory>
+#include <stdlib.h>
#include <sys/types.h>
#include <log/log.h>
-#include <log/log_read.h>
-#include <utils/List.h>
+#include <utils/BasicHashtable.h>
+
+#include "LogBufferElement.h"
#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 PidStatistics {
- const pid_t pid;
-
- // Total
- size_t mSizesTotal;
- size_t mElementsTotal;
- // Current
- size_t mSizes;
- size_t mElements;
-
- char *name;
- bool mGone;
-
+template <typename TKey, typename TEntry>
+class LogHashtable : public android::BasicHashtable<TKey, TEntry> {
public:
- static const pid_t gone = (pid_t) -1;
+ std::unique_ptr<const TEntry *[]> sort(size_t n) {
+ if (!n) {
+ std::unique_ptr<const TEntry *[]> sorted(NULL);
+ return sorted;
+ }
- PidStatistics(pid_t pid, char *name = NULL);
- PidStatistics(const PidStatistics ©);
- ~PidStatistics();
+ const TEntry **retval = new const TEntry* [n];
+ memset(retval, 0, sizeof(*retval) * n);
- pid_t getPid() const { return pid; }
- bool pidGone();
- char *getName() const { return name; }
- void setName(char *name);
+ ssize_t index = -1;
+ while ((index = android::BasicHashtable<TKey, TEntry>::next(index)) >= 0) {
+ const TEntry &entry = android::BasicHashtable<TKey, TEntry>::entryAt(index);
+ size_t s = entry.getSizes();
+ ssize_t i = n - 1;
+ while ((!retval[i] || (s > retval[i]->getSizes())) && (--i >= 0))
+ ;
+ if (++i < (ssize_t)n) {
+ size_t b = n - i - 1;
+ if (b) {
+ memmove(&retval[i+1], &retval[i], b * sizeof(retval[0]));
+ }
+ retval[i] = &entry;
+ }
+ }
+ std::unique_ptr<const TEntry *[]> sorted(retval);
+ return sorted;
+ }
- void add(unsigned short size);
- bool subtract(unsigned short size); // returns true if stats and PID gone
- void addTotal(size_t size, size_t element);
+ // 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;
+ }
- size_t sizes() const { return mSizes; }
- size_t elements() const { return mElements; }
+ ssize_t next(ssize_t index) {
+ return android::BasicHashtable<TKey, TEntry>::next(index);
+ }
- size_t sizesTotal() const { return mSizesTotal; }
- size_t elementsTotal() const { return mElementsTotal; }
+ size_t add(TKey key, LogBufferElement *e) {
+ android::hash_t hash = android::hash_type(key);
+ ssize_t index = android::BasicHashtable<TKey, TEntry>::find(-1, hash, key);
+ if (index == -1) {
+ return android::BasicHashtable<TKey, TEntry>::add(hash, TEntry(e));
+ }
+ android::BasicHashtable<TKey, TEntry>::editEntryAt(index).add(e);
+ return index;
+ }
- // helper
- static char *pidToName(pid_t pid);
+ inline size_t add(TKey key) {
+ android::hash_t hash = android::hash_type(key);
+ ssize_t index = android::BasicHashtable<TKey, TEntry>::find(-1, hash, key);
+ if (index == -1) {
+ return android::BasicHashtable<TKey, TEntry>::add(hash, TEntry(key));
+ }
+ android::BasicHashtable<TKey, TEntry>::editEntryAt(index).add(key);
+ return index;
+ }
+
+ void subtract(TKey key, LogBufferElement *e) {
+ ssize_t index = android::BasicHashtable<TKey, TEntry>::find(-1, android::hash_type(key), key);
+ if ((index != -1)
+ && android::BasicHashtable<TKey, TEntry>::editEntryAt(index).subtract(e)) {
+ android::BasicHashtable<TKey, TEntry>::removeAt(index);
+ }
+ }
+
+ inline void drop(TKey key, LogBufferElement *e) {
+ ssize_t index = android::BasicHashtable<TKey, TEntry>::find(-1, android::hash_type(key), key);
+ if (index != -1) {
+ android::BasicHashtable<TKey, TEntry>::editEntryAt(index).drop(e);
+ }
+ }
+
};
-typedef android::List<PidStatistics *> PidStatisticsCollection;
+struct EntryBase {
+ size_t size;
-class UidStatistics {
+ EntryBase():size(0) { }
+ EntryBase(LogBufferElement *e):size(e->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; }
+};
+
+struct EntryBaseDropped : public EntryBase {
+ size_t dropped;
+
+ EntryBaseDropped():dropped(0) { }
+ EntryBaseDropped(LogBufferElement *e):EntryBase(e),dropped(e->getDropped()){ }
+
+ size_t getDropped() const { return dropped; }
+
+ inline void add(LogBufferElement *e) {
+ dropped += e->getDropped();
+ EntryBase::add(e);
+ }
+ inline bool subtract(LogBufferElement *e) {
+ dropped -= e->getDropped();
+ return EntryBase::subtract(e) && !dropped;
+ }
+ inline void drop(LogBufferElement *e) {
+ dropped += 1;
+ EntryBase::subtract(e);
+ }
+};
+
+struct UidEntry : public EntryBaseDropped {
const uid_t uid;
- PidStatisticsCollection Pids;
+ UidEntry(LogBufferElement *e):EntryBaseDropped(e),uid(e->getUid()) { }
- void insert(PidStatisticsCollection::iterator i, PidStatistics *p)
- { Pids.insert(i, p); }
- void push_back(PidStatistics *p) { Pids.push_back(p); }
-
- size_t mSizes;
- size_t mElements;
-
-public:
- UidStatistics(uid_t uid);
- ~UidStatistics();
-
- PidStatisticsCollection::iterator begin() { return Pids.begin(); }
- PidStatisticsCollection::iterator end() { return Pids.end(); }
- PidStatisticsCollection::iterator erase(PidStatisticsCollection::iterator i)
- { return Pids.erase(i); }
-
- uid_t getUid() { return uid; }
-
- void add(unsigned short size, pid_t pid);
- void subtract(unsigned short size, pid_t pid);
- void sort();
-
- static const pid_t pid_all = (pid_t) -1;
-
- // fast track current value
- size_t sizes() const { return mSizes; };
- size_t elements() const { return mElements; };
-
- // statistical track
- size_t sizes(pid_t pid);
- size_t elements(pid_t pid);
-
- size_t sizesTotal(pid_t pid = pid_all);
- size_t elementsTotal(pid_t pid = pid_all);
-
- // helper
- static char *pidToName(pid_t pid) { return PidStatistics::pidToName(pid); }
+ inline const uid_t&getKey() const { return uid; }
};
-typedef android::List<UidStatistics *> UidStatisticsCollection;
+namespace android {
+uid_t pidToUid(pid_t pid);
+}
-class LidStatistics {
- UidStatisticsCollection Uids;
+struct PidEntry : public EntryBaseDropped {
+ const pid_t pid;
+ uid_t uid;
+ char *name;
-public:
- LidStatistics();
- ~LidStatistics();
+ 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() { free(name); }
- UidStatisticsCollection::iterator begin() { return Uids.begin(); }
- UidStatisticsCollection::iterator end() { return Uids.end(); }
+ const pid_t&getKey() const { return pid; }
+ const uid_t&getUid() const { return uid; }
+ const char*getName() const { return name; }
- void add(unsigned short size, uid_t uid, pid_t pid);
- void subtract(unsigned short size, uid_t uid, pid_t pid);
- void sort();
+ inline void add(pid_t p) {
+ if (name && !strncmp(name, "zygote", 6)) {
+ free(name);
+ name = NULL;
+ }
+ if (!name) {
+ char *n = android::pidToName(p);
+ if (n) {
+ name = n;
+ }
+ }
+ }
- static const pid_t pid_all = (pid_t) -1;
- static const uid_t uid_all = (uid_t) -1;
+ inline void add(LogBufferElement *e) {
+ uid_t u = e->getUid();
+ if (getUid() != u) {
+ uid = u;
+ free(name);
+ name = android::pidToName(e->getPid());
+ } else {
+ add(e->getPid());
+ }
+ EntryBaseDropped::add(e);
+ }
+};
- size_t sizes(uid_t uid = uid_all, pid_t pid = pid_all);
- size_t elements(uid_t uid = uid_all, pid_t pid = pid_all);
+struct TidEntry : public EntryBaseDropped {
+ const pid_t tid;
+ uid_t uid;
+ char *name;
- size_t sizesTotal(uid_t uid = uid_all, pid_t pid = pid_all);
- size_t elementsTotal(uid_t uid = uid_all, pid_t pid = pid_all);
+ 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() { 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) {
+ if (name && !strncmp(name, "zygote", 6)) {
+ free(name);
+ name = NULL;
+ }
+ if (!name) {
+ char *n = android::tidToName(t);
+ if (n) {
+ name = n;
+ }
+ }
+ }
+
+ inline void add(LogBufferElement *e) {
+ uid_t u = e->getUid();
+ if (getUid() != u) {
+ uid = u;
+ free(name);
+ name = android::tidToName(e->getTid());
+ } else {
+ add(e->getTid());
+ }
+ EntryBaseDropped::add(e);
+ }
+};
+
+struct TagEntry : public EntryBase {
+ const uint32_t tag;
+ uid_t uid;
+
+ TagEntry(LogBufferElement *e):
+ EntryBase(e),
+ tag(e->getTag()),
+ uid(e->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) {
+ uid = -1;
+ }
+ EntryBase::add(e);
+ }
};
// Log Statistics
class LogStatistics {
- LidStatistics LogIds[LOG_ID_MAX];
-
size_t mSizes[LOG_ID_MAX];
size_t mElements[LOG_ID_MAX];
+ size_t mSizesTotal[LOG_ID_MAX];
+ size_t mElementsTotal[LOG_ID_MAX];
+ bool enable;
- bool mStatistics;
+ // uid to size list
+ typedef LogHashtable<uid_t, UidEntry> uidTable_t;
+ uidTable_t uidTable[LOG_ID_MAX];
- static const unsigned short mBuckets[14];
- log_time mMinimum[sizeof(mBuckets) / sizeof(mBuckets[0])];
+ // pid to uid list
+ typedef LogHashtable<pid_t, PidEntry> pidTable_t;
+ pidTable_t pidTable;
+
+ // tid to uid list
+ typedef LogHashtable<pid_t, TidEntry> tidTable_t;
+ tidTable_t tidTable;
+
+ // tag list
+ typedef LogHashtable<uint32_t, TagEntry> tagTable_t;
+ tagTable_t tagTable;
public:
- const log_time start;
-
LogStatistics();
- LidStatistics &id(log_id_t log_id) { return LogIds[log_id]; }
+ void enableStatistics() { enable = true; }
- void enableStatistics() { mStatistics = true; }
+ void add(LogBufferElement *entry);
+ 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()]; }
- void add(unsigned short size, log_id_t log_id, uid_t uid, pid_t pid);
- void subtract(unsigned short size, log_id_t log_id, uid_t uid, pid_t pid);
- void sort();
+ std::unique_ptr<const UidEntry *[]> sort(size_t n, log_id i) { return uidTable[i].sort(n); }
// 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]; }
-
- // statistical track
- static const log_id_t log_id_all = (log_id_t) -1;
- static const uid_t uid_all = (uid_t) -1;
- static const pid_t pid_all = (pid_t) -1;
-
- size_t sizes(log_id_t id, uid_t uid, pid_t pid = pid_all);
- size_t elements(log_id_t id, uid_t uid, pid_t pid = pid_all);
- size_t sizes() { return sizes(log_id_all, uid_all); }
- size_t elements() { return elements(log_id_all, uid_all); }
-
- size_t sizesTotal(log_id_t id = log_id_all,
- uid_t uid = uid_all,
- pid_t pid = pid_all);
- size_t elementsTotal(log_id_t id = log_id_all,
- uid_t uid = uid_all,
- pid_t pid = pid_all);
+ size_t sizesTotal(log_id_t id) const { return mSizesTotal[id]; }
+ size_t elementsTotal(log_id_t id) const { return mElementsTotal[id]; }
// *strp = malloc, balance with free
- void format(char **strp, uid_t uid, unsigned int logMask, log_time oldest);
+ void format(char **strp, uid_t uid, unsigned int logMask);
// helper
- static char *pidToName(pid_t pid) { return PidStatistics::pidToName(pid); }
+ char *pidToName(pid_t pid);
uid_t pidToUid(pid_t pid);
+ char *uidToName(uid_t uid);
};
#endif // _LOGD_LOG_STATISTICS_H__
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index 1b60b7e..ec67c07 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -26,24 +26,23 @@
LogTimeEntry::LogTimeEntry(LogReader &reader, SocketClient *client,
bool nonBlock, unsigned long tail,
unsigned int logMask, pid_t pid,
- uint64_t start)
- : mRefCount(1)
- , mRelease(false)
- , mError(false)
- , threadRunning(false)
- , mReader(reader)
- , mLogMask(logMask)
- , mPid(pid)
- , mCount(0)
- , mTail(tail)
- , mIndex(0)
- , mClient(client)
- , mStart(start)
- , mNonBlock(nonBlock)
- , mEnd(LogBufferElement::getCurrentSequence())
-{
- pthread_cond_init(&threadTriggeredCondition, NULL);
- cleanSkip_Locked();
+ uint64_t start) :
+ mRefCount(1),
+ mRelease(false),
+ mError(false),
+ threadRunning(false),
+ mReader(reader),
+ mLogMask(logMask),
+ mPid(pid),
+ mCount(0),
+ mTail(tail),
+ mIndex(0),
+ mClient(client),
+ mStart(start),
+ mNonBlock(nonBlock),
+ mEnd(LogBufferElement::getCurrentSequence()) {
+ pthread_cond_init(&threadTriggeredCondition, NULL);
+ cleanSkip_Locked();
}
void LogTimeEntry::startReader_Locked(void) {
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
index 6910854..277b3ca 100644
--- a/logd/LogWhiteBlackList.cpp
+++ b/logd/LogWhiteBlackList.cpp
@@ -15,7 +15,6 @@
*/
#include <ctype.h>
-#include <malloc.h>
#include <utils/String8.h>
@@ -23,10 +22,8 @@
// White and Black list
-Prune::Prune(uid_t uid, pid_t pid)
- : mUid(uid)
- , mPid(pid)
-{ }
+Prune::Prune(uid_t uid, pid_t pid) : mUid(uid), mPid(pid) {
+}
int Prune::cmp(uid_t uid, pid_t pid) const {
if ((mUid == uid_all) || (mUid == uid)) {
@@ -52,8 +49,7 @@
}
}
-PruneList::PruneList()
- : mWorstUidEnabled(false) {
+PruneList::PruneList() : mWorstUidEnabled(true) {
mNaughty.clear();
mNice.clear();
}
@@ -71,7 +67,7 @@
}
int PruneList::init(char *str) {
- mWorstUidEnabled = false;
+ mWorstUidEnabled = true;
PruneCollection::iterator it;
for (it = mNice.begin(); it != mNice.end();) {
delete (*it);
diff --git a/logd/LogWhiteBlackList.h b/logd/LogWhiteBlackList.h
index 769d651..5f60801 100644
--- a/logd/LogWhiteBlackList.h
+++ b/logd/LogWhiteBlackList.h
@@ -61,7 +61,9 @@
int init(char *str);
bool naughty(LogBufferElement *element);
+ bool naughty(void) { return !mNaughty.empty(); }
bool nice(LogBufferElement *element);
+ bool nice(void) { return !mNice.empty(); }
bool worstUidEnabled() const { return mWorstUidEnabled; }
// *strp is malloc'd, use free to release
diff --git a/logd/README.property b/logd/README.property
index 60542b2..ad7d0cd 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -4,9 +4,12 @@
logd.auditd bool true Enable selinux audit daemon
logd.auditd.dmesg bool true selinux audit messages duplicated and
sent on to dmesg log
+logd.klogd bool depends Enable klogd daemon
logd.statistics bool depends Enable logcat -S statistics.
-ro.config.low_ram bool false if true, logd.statistics default false
-ro.build.type string if user, logd.statistics default false
+ro.config.low_ram bool false if true, logd.statistics & logd.klogd
+ default false
+ro.build.type string if user, logd.statistics & logd.klogd
+ default false
persist.logd.size number 256K default size of the buffer for all
log ids at initial startup, at runtime
use: logcat -b all -G <value>
diff --git a/logd/event.logtags b/logd/event.logtags
index a63f034..db8c19b 100644
--- a/logd/event.logtags
+++ b/logd/event.logtags
@@ -34,3 +34,4 @@
# TODO: generate ".java" and ".h" files with integer constants from this file.
1003 auditd (avc|3)
+1004 logd (dropped|3)
diff --git a/logd/main.cpp b/logd/main.cpp
index a61beff..6db819e 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -35,12 +35,14 @@
#include <cutils/properties.h>
#include <cutils/sched_policy.h>
#include <cutils/sockets.h>
+#include <log/event_tag_map.h>
+#include <private/android_filesystem_config.h>
-#include "private/android_filesystem_config.h"
#include "CommandListener.h"
#include "LogBuffer.h"
#include "LogListener.h"
#include "LogAudit.h"
+#include "LogKlog.h"
#define KMSG_PRIORITY(PRI) \
'<', \
@@ -143,6 +145,10 @@
// write(fdDmesg, "I am here\n", 10);
static int fdDmesg = -1;
+static sem_t uidName;
+static uid_t uid;
+static char *name;
+
static sem_t reinit;
static bool reinit_running = false;
static LogBuffer *logBuf = NULL;
@@ -151,10 +157,45 @@
prctl(PR_SET_NAME, "logd.daemon");
set_sched_policy(0, SP_BACKGROUND);
- setgid(AID_LOGD);
- setuid(AID_LOGD);
+ setgid(AID_SYSTEM);
+ setuid(AID_SYSTEM);
while (reinit_running && !sem_wait(&reinit) && reinit_running) {
+
+ // uidToName Privileged Worker
+ if (uid) {
+ name = NULL;
+
+ FILE *fp = fopen("/data/system/packages.list", "r");
+ if (fp) {
+ // This simple parser is sensitive to format changes in
+ // frameworks/base/services/core/java/com/android/server/pm/Settings.java
+ // A dependency note has been added to that file to correct
+ // this parser.
+
+ char *buffer = NULL;
+ size_t len;
+ while (getline(&buffer, &len, fp) > 0) {
+ char *userId = strchr(buffer, ' ');
+ if (!userId) {
+ continue;
+ }
+ *userId = '\0';
+ unsigned long value = strtoul(userId + 1, NULL, 10);
+ if (value != uid) {
+ continue;
+ }
+ name = strdup(buffer);
+ break;
+ }
+ free(buffer);
+ fclose(fp);
+ }
+ uid = 0;
+ sem_post(&uidName);
+ continue;
+ }
+
if (fdDmesg >= 0) {
static const char reinit_message[] = { KMSG_PRIORITY(LOG_INFO),
'l', 'o', 'g', 'd', '.', 'd', 'a', 'e', 'm', 'o', 'n', ':',
@@ -171,12 +212,62 @@
return NULL;
}
+static sem_t sem_name;
+
+char *android::uidToName(uid_t u) {
+ if (!u || !reinit_running) {
+ return NULL;
+ }
+
+ sem_wait(&sem_name);
+
+ // Not multi-thread safe, we use sem_name to protect
+ uid = u;
+
+ name = NULL;
+ sem_post(&reinit);
+ sem_wait(&uidName);
+ char *ret = name;
+
+ sem_post(&sem_name);
+
+ return ret;
+}
+
// Serves as a global method to trigger reinitialization
// and as a function that can be provided to signal().
void reinit_signal_handler(int /*signal*/) {
sem_post(&reinit);
}
+// tagToName converts an events tag into a name
+const char *android::tagToName(uint32_t tag) {
+ static const EventTagMap *map;
+
+ if (!map) {
+ sem_wait(&sem_name);
+ if (!map) {
+ map = android_openEventTagMap(EVENT_TAG_MAP_FILE);
+ }
+ sem_post(&sem_name);
+ if (!map) {
+ return NULL;
+ }
+ }
+ return android_lookupEventTag(map, tag);
+}
+
+static bool property_get_bool_svelte(const char *key) {
+ bool not_user;
+ {
+ char property[PROPERTY_VALUE_MAX];
+ property_get("ro.build.type", property, "");
+ not_user = !!strcmp(property, "user");
+ }
+ return property_get_bool(key, not_user
+ && !property_get_bool("ro.config.low_ram", false));
+}
+
// Foreground waits for exit of the main persistent threads
// that are started here. The threads are created to manage
// UNIX domain client sockets for writing, reading and
@@ -184,6 +275,11 @@
// logging plugins like auditd and restart control. Additional
// transitory per-client threads are created for each reader.
int main(int argc, char *argv[]) {
+ int fdPmesg = -1;
+ bool klogd = property_get_bool_svelte("logd.klogd");
+ if (klogd) {
+ fdPmesg = open("/proc/kmsg", O_RDONLY | O_NDELAY);
+ }
fdDmesg = open("/dev/kmsg", O_WRONLY);
// issue reinit command. KISS argument parsing.
@@ -223,6 +319,8 @@
// Reinit Thread
sem_init(&reinit, 0, 0);
+ sem_init(&uidName, 0, 0);
+ sem_init(&sem_name, 0, 1);
pthread_attr_t attr;
if (!pthread_attr_init(&attr)) {
struct sched_param param;
@@ -258,14 +356,8 @@
signal(SIGHUP, reinit_signal_handler);
- {
- char property[PROPERTY_VALUE_MAX];
- property_get("ro.build.type", property, "");
- if (property_get_bool("logd.statistics",
- !!strcmp(property, "user")
- && !property_get_bool("ro.config.low_ram", false))) {
- logBuf->enableStatistics();
- }
+ if (property_get_bool_svelte("logd.statistics")) {
+ logBuf->enableStatistics();
}
// LogReader listens on /dev/socket/logdr. When a client
@@ -300,12 +392,18 @@
bool auditd = property_get_bool("logd.auditd", true);
+ LogAudit *al = NULL;
if (auditd) {
bool dmesg = property_get_bool("logd.auditd.dmesg", true);
+ al = new LogAudit(logBuf, reader, dmesg ? fdDmesg : -1);
+ }
- // failure is an option ... messages are in dmesg (required by standard)
- LogAudit *al = new LogAudit(logBuf, reader, dmesg ? fdDmesg : -1);
+ LogKlog *kl = NULL;
+ if (klogd) {
+ kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != NULL);
+ }
+ if (al || kl) {
int len = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
if (len > 0) {
len++;
@@ -315,14 +413,29 @@
buf[len - 1] = '\0';
- for(char *ptr, *tok = buf;
- (rc >= 0) && ((tok = strtok_r(tok, "\r\n", &ptr)));
- tok = NULL) {
- rc = al->log(tok);
+ if ((rc >= 0) && kl) {
+ kl->synchronize(buf);
+ }
+
+ for (char *ptr, *tok = buf;
+ (rc >= 0) && ((tok = strtok_r(tok, "\r\n", &ptr)));
+ tok = NULL) {
+ if (al) {
+ rc = al->log(tok);
+ }
+ if (kl) {
+ rc = kl->log(tok);
+ }
}
}
- if (al->startListener()) {
+ // failure is an option ... messages are in dmesg (required by standard)
+
+ if (kl && kl->startListener()) {
+ delete kl;
+ }
+
+ if (al && al->startListener()) {
delete al;
}
}
diff --git a/logd/tests/Android.mk b/logd/tests/Android.mk
index f851288..85ca4ac 100644
--- a/logd/tests/Android.mk
+++ b/logd/tests/Android.mk
@@ -46,7 +46,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE := $(test_module_prefix)unit-tests
LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_CFLAGS += $(test_c_flags)
LOCAL_SHARED_LIBRARIES := libcutils
LOCAL_SRC_FILES := $(test_src_files)
diff --git a/mkbootimg/bootimg.h b/mkbootimg/bootimg.h
index 9171d85..5ab6195 100644
--- a/mkbootimg/bootimg.h
+++ b/mkbootimg/bootimg.h
@@ -15,6 +15,8 @@
** limitations under the License.
*/
+#include <stdint.h>
+
#ifndef _BOOT_IMAGE_H_
#define _BOOT_IMAGE_H_
@@ -28,31 +30,31 @@
struct boot_img_hdr
{
- unsigned char magic[BOOT_MAGIC_SIZE];
+ uint8_t magic[BOOT_MAGIC_SIZE];
- unsigned kernel_size; /* size in bytes */
- unsigned kernel_addr; /* physical load addr */
+ uint32_t kernel_size; /* size in bytes */
+ uint32_t kernel_addr; /* physical load addr */
- unsigned ramdisk_size; /* size in bytes */
- unsigned ramdisk_addr; /* physical load addr */
+ uint32_t ramdisk_size; /* size in bytes */
+ uint32_t ramdisk_addr; /* physical load addr */
- unsigned second_size; /* size in bytes */
- unsigned second_addr; /* physical load addr */
+ uint32_t second_size; /* size in bytes */
+ uint32_t second_addr; /* physical load addr */
- unsigned tags_addr; /* physical addr for kernel tags */
- unsigned page_size; /* flash page size we assume */
- unsigned unused[2]; /* future expansion: should be 0 */
+ uint32_t tags_addr; /* physical addr for kernel tags */
+ uint32_t page_size; /* flash page size we assume */
+ uint32_t unused[2]; /* future expansion: should be 0 */
- unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */
+ uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
- unsigned char cmdline[BOOT_ARGS_SIZE];
+ uint8_t cmdline[BOOT_ARGS_SIZE];
- unsigned id[8]; /* timestamp / checksum / sha1 / etc */
+ uint32_t id[8]; /* timestamp / checksum / sha1 / etc */
/* Supplemental command line data; kept here to maintain
* binary compatibility with older versions of mkbootimg */
- unsigned char extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
-};
+ uint8_t extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
+} __attribute__((packed));
/*
** +-----------------+
diff --git a/mkbootimg/mkbootimg.c b/mkbootimg/mkbootimg.c
index fc92b4d..40e5261 100644
--- a/mkbootimg/mkbootimg.c
+++ b/mkbootimg/mkbootimg.c
@@ -21,6 +21,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
+#include <stdbool.h>
#include "mincrypt/sha.h"
#include "bootimg.h"
@@ -65,6 +66,7 @@
" [ --board <boardname> ]\n"
" [ --base <address> ]\n"
" [ --pagesize <pagesize> ]\n"
+ " [ --id ]\n"
" -o|--output <filename>\n"
);
return 1;
@@ -74,6 +76,14 @@
static unsigned char padding[16384] = { 0, };
+static void print_id(const uint8_t *id, size_t id_len) {
+ printf("0x");
+ for (unsigned i = 0; i < id_len; i++) {
+ printf("%02x", id[i]);
+ }
+ printf("\n");
+}
+
int write_padding(int fd, unsigned pagesize, unsigned itemsize)
{
unsigned pagemask = pagesize - 1;
@@ -96,24 +106,24 @@
{
boot_img_hdr hdr;
- char *kernel_fn = 0;
- void *kernel_data = 0;
- char *ramdisk_fn = 0;
- void *ramdisk_data = 0;
- char *second_fn = 0;
- void *second_data = 0;
+ char *kernel_fn = NULL;
+ void *kernel_data = NULL;
+ char *ramdisk_fn = NULL;
+ void *ramdisk_data = NULL;
+ char *second_fn = NULL;
+ void *second_data = NULL;
char *cmdline = "";
- char *bootimg = 0;
+ char *bootimg = NULL;
char *board = "";
- unsigned pagesize = 2048;
+ uint32_t pagesize = 2048;
int fd;
SHA_CTX ctx;
const uint8_t* sha;
- unsigned base = 0x10000000;
- unsigned kernel_offset = 0x00008000;
- unsigned ramdisk_offset = 0x01000000;
- unsigned second_offset = 0x00f00000;
- unsigned tags_offset = 0x00000100;
+ uint32_t base = 0x10000000U;
+ uint32_t kernel_offset = 0x00008000U;
+ uint32_t ramdisk_offset = 0x01000000U;
+ uint32_t second_offset = 0x00f00000U;
+ uint32_t tags_offset = 0x00000100U;
size_t cmdlen;
argc--;
@@ -121,42 +131,48 @@
memset(&hdr, 0, sizeof(hdr));
+ bool get_id = false;
while(argc > 0){
char *arg = argv[0];
- char *val = argv[1];
- if(argc < 2) {
- return usage();
- }
- argc -= 2;
- argv += 2;
- if(!strcmp(arg, "--output") || !strcmp(arg, "-o")) {
- bootimg = val;
- } else if(!strcmp(arg, "--kernel")) {
- kernel_fn = val;
- } else if(!strcmp(arg, "--ramdisk")) {
- ramdisk_fn = val;
- } else if(!strcmp(arg, "--second")) {
- second_fn = val;
- } else if(!strcmp(arg, "--cmdline")) {
- cmdline = val;
- } else if(!strcmp(arg, "--base")) {
- base = strtoul(val, 0, 16);
- } else if(!strcmp(arg, "--kernel_offset")) {
- kernel_offset = strtoul(val, 0, 16);
- } else if(!strcmp(arg, "--ramdisk_offset")) {
- ramdisk_offset = strtoul(val, 0, 16);
- } else if(!strcmp(arg, "--second_offset")) {
- second_offset = strtoul(val, 0, 16);
- } else if(!strcmp(arg, "--tags_offset")) {
- tags_offset = strtoul(val, 0, 16);
- } else if(!strcmp(arg, "--board")) {
- board = val;
- } else if(!strcmp(arg,"--pagesize")) {
- pagesize = strtoul(val, 0, 10);
- if ((pagesize != 2048) && (pagesize != 4096)
- && (pagesize != 8192) && (pagesize != 16384)) {
- fprintf(stderr,"error: unsupported page size %d\n", pagesize);
- return -1;
+ if (!strcmp(arg, "--id")) {
+ get_id = true;
+ argc -= 1;
+ argv += 1;
+ } else if(argc >= 2) {
+ char *val = argv[1];
+ argc -= 2;
+ argv += 2;
+ if(!strcmp(arg, "--output") || !strcmp(arg, "-o")) {
+ bootimg = val;
+ } else if(!strcmp(arg, "--kernel")) {
+ kernel_fn = val;
+ } else if(!strcmp(arg, "--ramdisk")) {
+ ramdisk_fn = val;
+ } else if(!strcmp(arg, "--second")) {
+ second_fn = val;
+ } else if(!strcmp(arg, "--cmdline")) {
+ cmdline = val;
+ } else if(!strcmp(arg, "--base")) {
+ base = strtoul(val, 0, 16);
+ } else if(!strcmp(arg, "--kernel_offset")) {
+ kernel_offset = strtoul(val, 0, 16);
+ } else if(!strcmp(arg, "--ramdisk_offset")) {
+ ramdisk_offset = strtoul(val, 0, 16);
+ } else if(!strcmp(arg, "--second_offset")) {
+ second_offset = strtoul(val, 0, 16);
+ } else if(!strcmp(arg, "--tags_offset")) {
+ tags_offset = strtoul(val, 0, 16);
+ } else if(!strcmp(arg, "--board")) {
+ board = val;
+ } else if(!strcmp(arg,"--pagesize")) {
+ pagesize = strtoul(val, 0, 10);
+ if ((pagesize != 2048) && (pagesize != 4096)
+ && (pagesize != 8192) && (pagesize != 16384)) {
+ fprintf(stderr,"error: unsupported page size %d\n", pagesize);
+ return -1;
+ }
+ } else {
+ return usage();
}
} else {
return usage();
@@ -266,6 +282,10 @@
if(write_padding(fd, pagesize, hdr.second_size)) goto fail;
}
+ if (get_id) {
+ print_id((uint8_t *) hdr.id, sizeof(hdr.id));
+ }
+
return 0;
fail:
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 3ecb1db..7ab76b8 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -26,7 +26,7 @@
#
# create some directories (some are mount points)
LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
- sbin dev proc sys system data)
+ sbin dev proc sys system data oem)
include $(BUILD_SYSTEM)/base_rules.mk
diff --git a/rootdir/etc/mountd.conf b/rootdir/etc/mountd.conf
deleted file mode 100644
index 094a2c7..0000000
--- a/rootdir/etc/mountd.conf
+++ /dev/null
@@ -1,19 +0,0 @@
-## mountd configuration file
-
-## add a mount entry for each mount point to be managed by mountd
-mount {
- ## root block device with partition map or raw FAT file system
- block_device /dev/block/mmcblk0
-
- ## mount point for block device
- mount_point /sdcard
-
- ## true if this mount point can be shared via USB mass storage
- enable_ums true
-
- ## path to the UMS driver file for specifying the block device path
- ## use this for the mass_storage function driver
- driver_store_path /sys/devices/platform/usb_mass_storage/lun0/file
- ## use this for android_usb composite gadget driver
- ##driver_store_path /sys/devices/platform/msm_hsusb/gadget/lun0/file
-}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index bc36c3e..9fe1b4f 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -14,13 +14,6 @@
# Set init and its forked children's oom_adj.
write /proc/1/oom_score_adj -1000
- # Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls.
- write /sys/fs/selinux/checkreqprot 0
-
- # Set the security context for the init process.
- # This should occur before anything else (e.g. ueventd) is started.
- setcon u:r:init:s0
-
# Set the security context of /adb_keys if present.
restorecon /adb_keys
@@ -32,8 +25,6 @@
on init
sysclktz 0
- loglevel 3
-
# Backward compatibility.
symlink /system/etc /etc
symlink /sys/kernel/debug /d
@@ -164,6 +155,7 @@
# Load properties from /system/ + /factory after fs mount.
on load_all_props_action
load_all_props
+ start logd
start logd-reinit
# Indicate to fw loaders that the relevant mounts are up.
@@ -190,6 +182,7 @@
on post-fs
+ start logd
# once everything is setup, no need to modify /
mount rootfs rootfs / ro remount
# mount shared so changes propagate into child namespaces
@@ -233,6 +226,11 @@
# We restorecon /data in case the userdata partition has been reset.
restorecon /data
+ # Make sure we have the device encryption key
+ start logd
+ start vold
+ installkey /data
+
# Start bootcharting as soon as possible after the data partition is
# mounted to collect more data.
mkdir /data/bootchart 0755 shell shell
@@ -247,6 +245,7 @@
mkdir /data/misc/bluedroid 0770 bluetooth net_bt_stack
mkdir /data/misc/bluetooth 0770 system system
mkdir /data/misc/keystore 0700 keystore keystore
+ mkdir /data/misc/gatekeeper 0700 system system
mkdir /data/misc/keychain 0771 system system
mkdir /data/misc/net 0750 root shell
mkdir /data/misc/radio 0770 system radio
@@ -261,6 +260,7 @@
mkdir /data/misc/ethernet 0770 system system
mkdir /data/misc/dhcp 0770 dhcp dhcp
mkdir /data/misc/user 0771 root root
+ mkdir /data/misc/perfprofd 0775 root root
# give system access to wpa_supplicant.conf for backup and restore
chmod 0660 /data/misc/wifi/wpa_supplicant.conf
mkdir /data/local 0751 root root
@@ -305,12 +305,24 @@
# Separate location for storing security policy files on data
mkdir /data/security 0711 system system
+ # Create all remaining /data root dirs so that they are made through init
+ # and get proper encryption policy installed
+ mkdir /data/backup 0700 system system
+ mkdir /data/media 0770 media_rw media_rw
+ mkdir /data/ss 0700 system system
+ mkdir /data/system 0775 system system
+ mkdir /data/system/heapdump 0700 system system
+ mkdir /data/user 0711 system system
+
# Reload policy from /data/security if present.
setprop selinux.reload_policy 1
# Set SELinux security contexts on upgrade or policy update.
restorecon_recursive /data
+ # Check any timezone data in /data is newer than the copy in /system, delete if not.
+ exec u:r:tzdatacheck:s0 system system -- /system/bin/tzdatacheck /system/usr/share/zoneinfo /data/misc/zoneinfo
+
# If there is no fs-post-data action in the init.<device>.rc file, you
# must uncomment this line, otherwise encrypted filesystems
# won't work.
@@ -431,6 +443,7 @@
on property:vold.decrypt=trigger_load_persist_props
load_persist_props
+ start logd
start logd-reinit
on property:vold.decrypt=trigger_post_fs_data
@@ -631,3 +644,11 @@
class main
disabled
oneshot
+
+on property:ro.debuggable=1
+ start perfprofd
+
+service perfprofd /system/xbin/perfprofd
+ disabled
+ user root
+ oneshot
diff --git a/run-as/package.c b/run-as/package.c
index 4f8f3a7..9e1f5bb 100644
--- a/run-as/package.c
+++ b/run-as/package.c
@@ -16,9 +16,11 @@
*/
#include <errno.h>
#include <fcntl.h>
-#include <unistd.h>
-#include <sys/stat.h>
+#include <string.h>
#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
#include <private/android_filesystem_config.h>
#include "package.h"
diff --git a/run-as/run-as.c b/run-as/run-as.c
index cc05e63..368b8f1 100644
--- a/run-as/run-as.c
+++ b/run-as/run-as.c
@@ -15,22 +15,25 @@
** limitations under the License.
*/
-#define PROGNAME "run-as"
-#define LOG_TAG PROGNAME
+#define PROGNAME "run-as"
+#define LOG_TAG PROGNAME
+#include <dirent.h>
+#include <errno.h>
+#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/types.h>
+#include <sys/capability.h>
+#include <sys/cdefs.h>
#include <sys/stat.h>
-#include <dirent.h>
-#include <errno.h>
-#include <unistd.h>
+#include <sys/types.h>
#include <time.h>
-#include <stdarg.h>
+#include <unistd.h>
-#include <selinux/android.h>
#include <private/android_filesystem_config.h>
+#include <selinux/android.h>
+
#include "package.h"
/*
@@ -83,37 +86,37 @@
* - Run the 'gdbserver' binary executable to allow native debugging
*/
-static void
-usage(void)
-{
- const char* str = "Usage: " PROGNAME " <package-name> <command> [<args>]\n\n";
- write(1, str, strlen(str));
- exit(1);
-}
-
-
-static void
+__noreturn static void
panic(const char* format, ...)
{
va_list args;
+ int e = errno;
fprintf(stderr, "%s: ", PROGNAME);
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
- exit(1);
+ exit(e ? -e : 1);
}
+static void
+usage(void)
+{
+ panic("Usage:\n " PROGNAME " <package-name> <command> [<args>]\n");
+}
int main(int argc, char **argv)
{
const char* pkgname;
int myuid, uid, gid;
PackageInfo info;
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata[2];
/* check arguments */
- if (argc < 2)
+ if (argc < 2) {
usage();
+ }
/* check userid of caller - must be 'shell' or 'root' */
myuid = getuid();
@@ -121,29 +124,37 @@
panic("only 'shell' or 'root' users can run this program\n");
}
- /* retrieve package information from system */
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ capdata[CAP_TO_INDEX(CAP_SETUID)].effective |= CAP_TO_MASK(CAP_SETUID);
+ capdata[CAP_TO_INDEX(CAP_SETGID)].effective |= CAP_TO_MASK(CAP_SETGID);
+ capdata[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);
+ capdata[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);
+
+ if (capset(&capheader, &capdata[0]) < 0) {
+ panic("Could not set capabilities: %s\n", strerror(errno));
+ }
+
+ /* retrieve package information from system (does setegid) */
pkgname = argv[1];
if (get_package_info(pkgname, &info) < 0) {
panic("Package '%s' is unknown\n", pkgname);
- return 1;
}
/* reject system packages */
if (info.uid < AID_APP) {
panic("Package '%s' is not an application\n", pkgname);
- return 1;
}
/* reject any non-debuggable package */
if (!info.isDebuggable) {
panic("Package '%s' is not debuggable\n", pkgname);
- return 1;
}
/* check that the data directory path is valid */
if (check_data_path(info.dataDir, info.uid) < 0) {
panic("Package '%s' has corrupt installation\n", pkgname);
- return 1;
}
/* Ensure that we change all real/effective/saved IDs at the
@@ -152,38 +163,30 @@
uid = gid = info.uid;
if(setresgid(gid,gid,gid) || setresuid(uid,uid,uid)) {
panic("Permission denied\n");
- return 1;
+ }
+
+ /* Required if caller has uid and gid all non-zero */
+ memset(&capdata, 0, sizeof(capdata));
+ if (capset(&capheader, &capdata[0]) < 0) {
+ panic("Could not clear all capabilities: %s\n", strerror(errno));
}
if (selinux_android_setcontext(uid, 0, info.seinfo, pkgname) < 0) {
- panic("Could not set SELinux security context: %s\n", strerror(errno));
- return 1;
+ panic("Could not set SELinux security context: %s\n", strerror(errno));
}
/* cd into the data directory */
- {
- int ret;
- do {
- ret = chdir(info.dataDir);
- } while (ret < 0 && errno == EINTR);
-
- if (ret < 0) {
- panic("Could not cd to package's data directory: %s\n", strerror(errno));
- return 1;
- }
+ if (TEMP_FAILURE_RETRY(chdir(info.dataDir)) < 0) {
+ panic("Could not cd to package's data directory: %s\n", strerror(errno));
}
/* User specified command for exec. */
- if (argc >= 3 ) {
- if (execvp(argv[2], argv+2) < 0) {
- panic("exec failed for %s Error:%s\n", argv[2], strerror(errno));
- return -errno;
- }
+ if ((argc >= 3) && (execvp(argv[2], argv+2) < 0)) {
+ panic("exec failed for %s: %s\n", argv[2], strerror(errno));
}
/* Default exec shell. */
execlp("/system/bin/sh", "sh", NULL);
- panic("exec failed\n");
- return 1;
+ panic("exec failed: %s\n", strerror(errno));
}
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
index 4d50bf0..893c0dc 100644
--- a/sdcard/sdcard.c
+++ b/sdcard/sdcard.c
@@ -199,6 +199,8 @@
* position. Used to support things like OBB. */
char* graft_path;
size_t graft_pathlen;
+
+ bool deleted;
};
static int str_hash(void *key) {
@@ -631,6 +633,8 @@
node->ino = fuse->inode_ctr++;
node->gen = fuse->next_generation++;
+ node->deleted = false;
+
derive_permissions_locked(fuse, parent, node);
acquire_node_locked(node);
add_node_to_parent_locked(node, parent);
@@ -704,7 +708,7 @@
* must be considered distinct even if they refer to the same
* underlying file as otherwise operations such as "mv x x"
* will not work because the source and target nodes are the same. */
- if (!strcmp(name, node->name)) {
+ if (!strcmp(name, node->name) && !node->deleted) {
return node;
}
}
@@ -1070,6 +1074,7 @@
{
bool has_rw;
struct node* parent_node;
+ struct node* child_node;
char parent_path[PATH_MAX];
char child_path[PATH_MAX];
@@ -1091,6 +1096,12 @@
if (unlink(child_path) < 0) {
return -errno;
}
+ pthread_mutex_lock(&fuse->lock);
+ child_node = lookup_child_by_name_locked(parent_node, name);
+ if (child_node) {
+ child_node->deleted = true;
+ }
+ pthread_mutex_unlock(&fuse->lock);
return 0;
}
@@ -1098,6 +1109,7 @@
const struct fuse_in_header* hdr, const char* name)
{
bool has_rw;
+ struct node* child_node;
struct node* parent_node;
char parent_path[PATH_MAX];
char child_path[PATH_MAX];
@@ -1120,6 +1132,12 @@
if (rmdir(child_path) < 0) {
return -errno;
}
+ pthread_mutex_lock(&fuse->lock);
+ child_node = lookup_child_by_name_locked(parent_node, name);
+ if (child_node) {
+ child_node->deleted = true;
+ }
+ pthread_mutex_unlock(&fuse->lock);
return 0;
}
@@ -1857,7 +1875,7 @@
struct fuse fuse;
/* cleanup from previous instance, if necessary */
- umount2(dest_path, 2);
+ umount2(dest_path, MNT_DETACH);
fd = open("/dev/fuse", O_RDWR);
if (fd < 0){
@@ -1869,7 +1887,8 @@
"fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d",
fd, uid, gid);
- res = mount("/dev/fuse", dest_path, "fuse", MS_NOSUID | MS_NODEV | MS_NOEXEC, opts);
+ res = mount("/dev/fuse", dest_path, "fuse", MS_NOSUID | MS_NODEV | MS_NOEXEC |
+ MS_NOATIME, opts);
if (res < 0) {
ERROR("cannot mount fuse filesystem: %s\n", strerror(errno));
goto error;
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index b822ea0..ad99a39 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -23,14 +23,12 @@
upstream-netbsd/lib/libutil/raise_default_signal.c
LOCAL_CFLAGS += $(common_cflags) -Dmain=dd_main -DNO_CONV
LOCAL_MODULE := libtoolbox_dd
-LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := upstream-netbsd/usr.bin/du/du.c
LOCAL_CFLAGS += $(common_cflags) -Dmain=du_main
LOCAL_MODULE := libtoolbox_du
-LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
include $(BUILD_STATIC_LIBRARY)
@@ -43,11 +41,9 @@
OUR_TOOLS := \
df \
getevent \
- getprop \
iftop \
ioctl \
ionice \
- load_policy \
log \
ls \
lsof \
@@ -57,23 +53,16 @@
ps \
prlimit \
renice \
- restorecon \
- route \
- runcon \
- schedtop \
sendevent \
- setprop \
start \
stop \
top \
- umount \
uptime \
watchprops \
ALL_TOOLS = $(BSD_TOOLS) $(OUR_TOOLS)
LOCAL_SRC_FILES := \
- dynarray.c \
toolbox.c \
$(patsubst %,%.c,$(OUR_TOOLS)) \
@@ -86,7 +75,6 @@
LOCAL_WHOLE_STATIC_LIBRARIES := $(patsubst %,libtoolbox_%,$(BSD_TOOLS))
LOCAL_MODULE := toolbox
-LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
# Install the symlinks.
LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,$(ALL_TOOLS),ln -sf toolbox $(TARGET_OUT)/bin/$(t);)
@@ -104,6 +92,14 @@
$(TOOLS_H):
$(transform-generated-source)
+$(LOCAL_PATH)/getevent.c: $(intermediates)/input.h-labels.h
+
+INPUT_H_LABELS_H := $(intermediates)/input.h-labels.h
+$(INPUT_H_LABELS_H): PRIVATE_LOCAL_PATH := $(LOCAL_PATH)
+$(INPUT_H_LABELS_H): PRIVATE_CUSTOM_TOOL = $(PRIVATE_LOCAL_PATH)/generate-input.h-labels.py > $@
+$(INPUT_H_LABELS_H): $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/generate-input.h-labels.py
+$(INPUT_H_LABELS_H):
+ $(transform-generated-source)
# We only want 'r' on userdebug and eng builds.
include $(CLEAR_VARS)
@@ -111,7 +107,6 @@
LOCAL_CFLAGS += $(common_cflags)
LOCAL_MODULE := r
LOCAL_MODULE_TAGS := debug
-LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
include $(BUILD_EXECUTABLE)
diff --git a/toolbox/dynarray.c b/toolbox/dynarray.c
deleted file mode 100644
index 47594e0..0000000
--- a/toolbox/dynarray.c
+++ /dev/null
@@ -1,104 +0,0 @@
-#include "dynarray.h"
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
-
-void
-dynarray_init( dynarray_t *a )
-{
- a->count = a->capacity = 0;
- a->items = NULL;
-}
-
-
-static void
-dynarray_reserve_more( dynarray_t *a, int count )
-{
- int old_cap = a->capacity;
- int new_cap = old_cap;
- const int max_cap = INT_MAX/sizeof(void*);
- void** new_items;
- int new_count = a->count + count;
-
- if (count <= 0)
- return;
-
- if (count > max_cap - a->count)
- abort();
-
- new_count = a->count + count;
-
- while (new_cap < new_count) {
- old_cap = new_cap;
- new_cap += (new_cap >> 2) + 4;
- if (new_cap < old_cap || new_cap > max_cap) {
- new_cap = max_cap;
- }
- }
- new_items = realloc(a->items, new_cap*sizeof(void*));
- if (new_items == NULL)
- abort();
-
- a->items = new_items;
- a->capacity = new_cap;
-}
-
-void
-dynarray_append( dynarray_t *a, void* item )
-{
- if (a->count >= a->capacity)
- dynarray_reserve_more(a, 1);
-
- a->items[a->count++] = item;
-}
-
-void
-dynarray_done( dynarray_t *a )
-{
- free(a->items);
- a->items = NULL;
- a->count = a->capacity = 0;
-}
-
-// string arrays
-
-void strlist_init( strlist_t *list )
-{
- dynarray_init(list);
-}
-
-void strlist_append_b( strlist_t *list, const void* str, size_t slen )
-{
- char *copy = malloc(slen+1);
- memcpy(copy, str, slen);
- copy[slen] = '\0';
- dynarray_append(list, copy);
-}
-
-void strlist_append_dup( strlist_t *list, const char *str)
-{
- strlist_append_b(list, str, strlen(str));
-}
-
-void strlist_done( strlist_t *list )
-{
- STRLIST_FOREACH(list, string, free(string));
- dynarray_done(list);
-}
-
-static int strlist_compare_strings(const void* a, const void* b)
-{
- const char *sa = *(const char **)a;
- const char *sb = *(const char **)b;
- return strcmp(sa, sb);
-}
-
-void strlist_sort( strlist_t *list )
-{
- if (list->count > 0) {
- qsort(list->items,
- (size_t)list->count,
- sizeof(void*),
- strlist_compare_strings);
- }
-}
diff --git a/toolbox/dynarray.h b/toolbox/dynarray.h
deleted file mode 100644
index f73fb3b..0000000
--- a/toolbox/dynarray.h
+++ /dev/null
@@ -1,80 +0,0 @@
-#ifndef DYNARRAY_H
-#define DYNARRAY_H
-
-#include <stddef.h>
-
-/* simple dynamic array of pointers */
-typedef struct {
- int count;
- int capacity;
- void** items;
-} dynarray_t;
-
-#define DYNARRAY_INITIALIZER { 0, 0, NULL }
-
-void dynarray_init( dynarray_t *a );
-void dynarray_done( dynarray_t *a );
-
-void dynarray_append( dynarray_t *a, void* item );
-
-/* Used to iterate over a dynarray_t
- * _array :: pointer to the array
- * _item_type :: type of objects pointed to by the array
- * _item :: name of a local variable defined within the loop
- * with type '_item_type'
- * _stmnt :: C statement that will be executed in each iteration.
- *
- * You case use 'break' and 'continue' within _stmnt
- *
- * This macro is only intended for simple uses. I.e. do not add or
- * remove items from the array during iteration.
- */
-#define DYNARRAY_FOREACH_TYPE(_array,_item_type,_item,_stmnt) \
- do { \
- int _nn_##__LINE__ = 0; \
- for (;_nn_##__LINE__ < (_array)->count; ++ _nn_##__LINE__) { \
- _item_type _item = (_item_type)(_array)->items[_nn_##__LINE__]; \
- _stmnt; \
- } \
- } while (0)
-
-#define DYNARRAY_FOREACH(_array,_item,_stmnt) \
- DYNARRAY_FOREACH_TYPE(_array,void *,_item,_stmnt)
-
-/* Simple dynamic string arrays
- *
- * NOTE: A strlist_t owns the strings it references.
- */
-typedef dynarray_t strlist_t;
-
-#define STRLIST_INITIALIZER DYNARRAY_INITIALIZER
-
-/* Used to iterate over a strlist_t
- * _list :: pointer to strlist_t object
- * _string :: name of local variable name defined within the loop with
- * type 'char*'
- * _stmnt :: C statement executed in each iteration
- *
- * This macro is only intended for simple uses. Do not add or remove items
- * to/from the list during iteration.
- */
-#define STRLIST_FOREACH(_list,_string,_stmnt) \
- DYNARRAY_FOREACH_TYPE(_list,char *,_string,_stmnt)
-
-void strlist_init( strlist_t *list );
-
-/* note: strlist_done will free all the strings owned by the list */
-void strlist_done( strlist_t *list );
-
-/* append a new string made of the first 'slen' characters from 'str'
- * followed by a trailing zero.
- */
-void strlist_append_b( strlist_t *list, const void* str, size_t slen );
-
-/* append the copy of a given input string to a strlist_t */
-void strlist_append_dup( strlist_t *list, const char *str);
-
-/* sort the strings in a given list (using strcmp) */
-void strlist_sort( strlist_t *list );
-
-#endif /* DYNARRAY_H */
\ No newline at end of file
diff --git a/toolbox/generate-input.h-labels.py b/toolbox/generate-input.h-labels.py
new file mode 100755
index 0000000..ebb9588
--- /dev/null
+++ b/toolbox/generate-input.h-labels.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# pylint: disable=bad-indentation,bad-continuation
+
+import os
+import re
+
+input_prop_list = []
+ev_list = []
+syn_list = []
+key_list = []
+rel_list = []
+abs_list = []
+sw_list = []
+msc_list = []
+led_list = []
+rep_list = []
+snd_list = []
+mt_tool_list = []
+ff_status_list = []
+ff_list = []
+
+r = re.compile(r'#define\s+(\S+)\s+((?:0x)?\d+)')
+
+with open('bionic/libc/kernel/uapi/linux/input.h', 'r') as f:
+ for line in f:
+ m = r.match(line)
+ if m:
+ name = m.group(1)
+ if name.startswith("INPUT_PROP_"):
+ input_prop_list.append(name)
+ elif name.startswith("EV_"):
+ ev_list.append(name)
+ elif name.startswith("SYN_"):
+ syn_list.append(name)
+ elif name.startswith("KEY_") or name.startswith("BTN_"):
+ key_list.append(name)
+ elif name.startswith("REL_"):
+ rel_list.append(name)
+ elif name.startswith("ABS_"):
+ abs_list.append(name)
+ elif name.startswith("SW_"):
+ sw_list.append(name)
+ elif name.startswith("MSC_"):
+ msc_list.append(name)
+ elif name.startswith("LED_"):
+ led_list.append(name)
+ elif name.startswith("REP_"):
+ rep_list.append(name)
+ elif name.startswith("SND_"):
+ snd_list.append(name)
+ elif name.startswith("MT_TOOL_"):
+ mt_tool_list.append(name)
+ elif name.startswith("FF_STATUS_"):
+ ff_status_list.append(name)
+ elif name.startswith("FF_"):
+ ff_list.append(name)
+
+def Dump(struct_name, values):
+ print 'static struct label %s[] = {' % (struct_name)
+ for value in values:
+ print ' LABEL(%s),' % (value)
+ print ' LABEL_END,'
+ print '};'
+
+Dump("input_prop_labels", input_prop_list)
+Dump("ev_labels", ev_list)
+Dump("syn_labels", syn_list)
+Dump("key_labels", key_list)
+Dump("rel_labels", rel_list)
+Dump("abs_labels", abs_list)
+Dump("sw_labels", sw_list)
+Dump("msc_labels", msc_list)
+Dump("led_labels", led_list)
+Dump("rep_labels", rep_list)
+Dump("snd_labels", snd_list)
+Dump("mt_tool_labels", mt_tool_list)
+Dump("ff_status_labels", ff_status_list)
+Dump("ff_labels", ff_list)
diff --git a/toolbox/getevent.c b/toolbox/getevent.c
index c58eb5d..30053af 100644
--- a/toolbox/getevent.c
+++ b/toolbox/getevent.c
@@ -12,7 +12,25 @@
#include <errno.h>
#include <unistd.h>
-#include "getevent.h"
+struct label {
+ const char *name;
+ int value;
+};
+
+#define LABEL(constant) { #constant, constant }
+#define LABEL_END { NULL, -1 }
+
+static struct label key_value_labels[] = {
+ { "UP", 0 },
+ { "DOWN", 1 },
+ { "REPEAT", 2 },
+ LABEL_END,
+};
+
+#include "input.h-labels.h"
+
+#undef LABEL
+#undef LABEL_END
static struct pollfd *ufds;
static char **device_names;
diff --git a/toolbox/getevent.h b/toolbox/getevent.h
deleted file mode 100644
index 0482d04..0000000
--- a/toolbox/getevent.h
+++ /dev/null
@@ -1,727 +0,0 @@
-#include <linux/input.h>
-
-struct label {
- const char *name;
- int value;
-};
-
-#define LABEL(constant) { #constant, constant }
-#define LABEL_END { NULL, -1 }
-
-static struct label input_prop_labels[] = {
- LABEL(INPUT_PROP_POINTER),
- LABEL(INPUT_PROP_DIRECT),
- LABEL(INPUT_PROP_BUTTONPAD),
- LABEL(INPUT_PROP_SEMI_MT),
- LABEL_END,
-};
-
-static struct label ev_labels[] = {
- LABEL(EV_SYN),
- LABEL(EV_KEY),
- LABEL(EV_REL),
- LABEL(EV_ABS),
- LABEL(EV_MSC),
- LABEL(EV_SW),
- LABEL(EV_LED),
- LABEL(EV_SND),
- LABEL(EV_REP),
- LABEL(EV_FF),
- LABEL(EV_PWR),
- LABEL(EV_FF_STATUS),
- LABEL_END,
-};
-
-static struct label syn_labels[] = {
- LABEL(SYN_REPORT),
- LABEL(SYN_CONFIG),
- LABEL(SYN_MT_REPORT),
- LABEL(SYN_DROPPED),
- LABEL_END,
-};
-
-static struct label key_labels[] = {
- LABEL(KEY_RESERVED),
- LABEL(KEY_ESC),
- LABEL(KEY_1),
- LABEL(KEY_2),
- LABEL(KEY_3),
- LABEL(KEY_4),
- LABEL(KEY_5),
- LABEL(KEY_6),
- LABEL(KEY_7),
- LABEL(KEY_8),
- LABEL(KEY_9),
- LABEL(KEY_0),
- LABEL(KEY_MINUS),
- LABEL(KEY_EQUAL),
- LABEL(KEY_BACKSPACE),
- LABEL(KEY_TAB),
- LABEL(KEY_Q),
- LABEL(KEY_W),
- LABEL(KEY_E),
- LABEL(KEY_R),
- LABEL(KEY_T),
- LABEL(KEY_Y),
- LABEL(KEY_U),
- LABEL(KEY_I),
- LABEL(KEY_O),
- LABEL(KEY_P),
- LABEL(KEY_LEFTBRACE),
- LABEL(KEY_RIGHTBRACE),
- LABEL(KEY_ENTER),
- LABEL(KEY_LEFTCTRL),
- LABEL(KEY_A),
- LABEL(KEY_S),
- LABEL(KEY_D),
- LABEL(KEY_F),
- LABEL(KEY_G),
- LABEL(KEY_H),
- LABEL(KEY_J),
- LABEL(KEY_K),
- LABEL(KEY_L),
- LABEL(KEY_SEMICOLON),
- LABEL(KEY_APOSTROPHE),
- LABEL(KEY_GRAVE),
- LABEL(KEY_LEFTSHIFT),
- LABEL(KEY_BACKSLASH),
- LABEL(KEY_Z),
- LABEL(KEY_X),
- LABEL(KEY_C),
- LABEL(KEY_V),
- LABEL(KEY_B),
- LABEL(KEY_N),
- LABEL(KEY_M),
- LABEL(KEY_COMMA),
- LABEL(KEY_DOT),
- LABEL(KEY_SLASH),
- LABEL(KEY_RIGHTSHIFT),
- LABEL(KEY_KPASTERISK),
- LABEL(KEY_LEFTALT),
- LABEL(KEY_SPACE),
- LABEL(KEY_CAPSLOCK),
- LABEL(KEY_F1),
- LABEL(KEY_F2),
- LABEL(KEY_F3),
- LABEL(KEY_F4),
- LABEL(KEY_F5),
- LABEL(KEY_F6),
- LABEL(KEY_F7),
- LABEL(KEY_F8),
- LABEL(KEY_F9),
- LABEL(KEY_F10),
- LABEL(KEY_NUMLOCK),
- LABEL(KEY_SCROLLLOCK),
- LABEL(KEY_KP7),
- LABEL(KEY_KP8),
- LABEL(KEY_KP9),
- LABEL(KEY_KPMINUS),
- LABEL(KEY_KP4),
- LABEL(KEY_KP5),
- LABEL(KEY_KP6),
- LABEL(KEY_KPPLUS),
- LABEL(KEY_KP1),
- LABEL(KEY_KP2),
- LABEL(KEY_KP3),
- LABEL(KEY_KP0),
- LABEL(KEY_KPDOT),
- LABEL(KEY_ZENKAKUHANKAKU),
- LABEL(KEY_102ND),
- LABEL(KEY_F11),
- LABEL(KEY_F12),
- LABEL(KEY_RO),
- LABEL(KEY_KATAKANA),
- LABEL(KEY_HIRAGANA),
- LABEL(KEY_HENKAN),
- LABEL(KEY_KATAKANAHIRAGANA),
- LABEL(KEY_MUHENKAN),
- LABEL(KEY_KPJPCOMMA),
- LABEL(KEY_KPENTER),
- LABEL(KEY_RIGHTCTRL),
- LABEL(KEY_KPSLASH),
- LABEL(KEY_SYSRQ),
- LABEL(KEY_RIGHTALT),
- LABEL(KEY_LINEFEED),
- LABEL(KEY_HOME),
- LABEL(KEY_UP),
- LABEL(KEY_PAGEUP),
- LABEL(KEY_LEFT),
- LABEL(KEY_RIGHT),
- LABEL(KEY_END),
- LABEL(KEY_DOWN),
- LABEL(KEY_PAGEDOWN),
- LABEL(KEY_INSERT),
- LABEL(KEY_DELETE),
- LABEL(KEY_MACRO),
- LABEL(KEY_MUTE),
- LABEL(KEY_VOLUMEDOWN),
- LABEL(KEY_VOLUMEUP),
- LABEL(KEY_POWER),
- LABEL(KEY_KPEQUAL),
- LABEL(KEY_KPPLUSMINUS),
- LABEL(KEY_PAUSE),
- LABEL(KEY_SCALE),
- LABEL(KEY_KPCOMMA),
- LABEL(KEY_HANGEUL),
- LABEL(KEY_HANGUEL),
- LABEL(KEY_HANJA),
- LABEL(KEY_YEN),
- LABEL(KEY_LEFTMETA),
- LABEL(KEY_RIGHTMETA),
- LABEL(KEY_COMPOSE),
- LABEL(KEY_STOP),
- LABEL(KEY_AGAIN),
- LABEL(KEY_PROPS),
- LABEL(KEY_UNDO),
- LABEL(KEY_FRONT),
- LABEL(KEY_COPY),
- LABEL(KEY_OPEN),
- LABEL(KEY_PASTE),
- LABEL(KEY_FIND),
- LABEL(KEY_CUT),
- LABEL(KEY_HELP),
- LABEL(KEY_MENU),
- LABEL(KEY_CALC),
- LABEL(KEY_SETUP),
- LABEL(KEY_SLEEP),
- LABEL(KEY_WAKEUP),
- LABEL(KEY_FILE),
- LABEL(KEY_SENDFILE),
- LABEL(KEY_DELETEFILE),
- LABEL(KEY_XFER),
- LABEL(KEY_PROG1),
- LABEL(KEY_PROG2),
- LABEL(KEY_WWW),
- LABEL(KEY_MSDOS),
- LABEL(KEY_COFFEE),
- LABEL(KEY_SCREENLOCK),
- LABEL(KEY_DIRECTION),
- LABEL(KEY_CYCLEWINDOWS),
- LABEL(KEY_MAIL),
- LABEL(KEY_BOOKMARKS),
- LABEL(KEY_COMPUTER),
- LABEL(KEY_BACK),
- LABEL(KEY_FORWARD),
- LABEL(KEY_CLOSECD),
- LABEL(KEY_EJECTCD),
- LABEL(KEY_EJECTCLOSECD),
- LABEL(KEY_NEXTSONG),
- LABEL(KEY_PLAYPAUSE),
- LABEL(KEY_PREVIOUSSONG),
- LABEL(KEY_STOPCD),
- LABEL(KEY_RECORD),
- LABEL(KEY_REWIND),
- LABEL(KEY_PHONE),
- LABEL(KEY_ISO),
- LABEL(KEY_CONFIG),
- LABEL(KEY_HOMEPAGE),
- LABEL(KEY_REFRESH),
- LABEL(KEY_EXIT),
- LABEL(KEY_MOVE),
- LABEL(KEY_EDIT),
- LABEL(KEY_SCROLLUP),
- LABEL(KEY_SCROLLDOWN),
- LABEL(KEY_KPLEFTPAREN),
- LABEL(KEY_KPRIGHTPAREN),
- LABEL(KEY_NEW),
- LABEL(KEY_REDO),
- LABEL(KEY_F13),
- LABEL(KEY_F14),
- LABEL(KEY_F15),
- LABEL(KEY_F16),
- LABEL(KEY_F17),
- LABEL(KEY_F18),
- LABEL(KEY_F19),
- LABEL(KEY_F20),
- LABEL(KEY_F21),
- LABEL(KEY_F22),
- LABEL(KEY_F23),
- LABEL(KEY_F24),
- LABEL(KEY_PLAYCD),
- LABEL(KEY_PAUSECD),
- LABEL(KEY_PROG3),
- LABEL(KEY_PROG4),
- LABEL(KEY_DASHBOARD),
- LABEL(KEY_SUSPEND),
- LABEL(KEY_CLOSE),
- LABEL(KEY_PLAY),
- LABEL(KEY_FASTFORWARD),
- LABEL(KEY_BASSBOOST),
- LABEL(KEY_PRINT),
- LABEL(KEY_HP),
- LABEL(KEY_CAMERA),
- LABEL(KEY_SOUND),
- LABEL(KEY_QUESTION),
- LABEL(KEY_EMAIL),
- LABEL(KEY_CHAT),
- LABEL(KEY_SEARCH),
- LABEL(KEY_CONNECT),
- LABEL(KEY_FINANCE),
- LABEL(KEY_SPORT),
- LABEL(KEY_SHOP),
- LABEL(KEY_ALTERASE),
- LABEL(KEY_CANCEL),
- LABEL(KEY_BRIGHTNESSDOWN),
- LABEL(KEY_BRIGHTNESSUP),
- LABEL(KEY_MEDIA),
- LABEL(KEY_SWITCHVIDEOMODE),
- LABEL(KEY_KBDILLUMTOGGLE),
- LABEL(KEY_KBDILLUMDOWN),
- LABEL(KEY_KBDILLUMUP),
- LABEL(KEY_SEND),
- LABEL(KEY_REPLY),
- LABEL(KEY_FORWARDMAIL),
- LABEL(KEY_SAVE),
- LABEL(KEY_DOCUMENTS),
- LABEL(KEY_BATTERY),
- LABEL(KEY_BLUETOOTH),
- LABEL(KEY_WLAN),
- LABEL(KEY_UWB),
- LABEL(KEY_UNKNOWN),
- LABEL(KEY_VIDEO_NEXT),
- LABEL(KEY_VIDEO_PREV),
- LABEL(KEY_BRIGHTNESS_CYCLE),
- LABEL(KEY_BRIGHTNESS_ZERO),
- LABEL(KEY_DISPLAY_OFF),
- LABEL(KEY_WIMAX),
- LABEL(KEY_RFKILL),
- LABEL(BTN_0),
- LABEL(BTN_1),
- LABEL(BTN_2),
- LABEL(BTN_3),
- LABEL(BTN_4),
- LABEL(BTN_5),
- LABEL(BTN_6),
- LABEL(BTN_7),
- LABEL(BTN_8),
- LABEL(BTN_9),
- LABEL(BTN_LEFT),
- LABEL(BTN_RIGHT),
- LABEL(BTN_MIDDLE),
- LABEL(BTN_SIDE),
- LABEL(BTN_EXTRA),
- LABEL(BTN_FORWARD),
- LABEL(BTN_BACK),
- LABEL(BTN_TASK),
- LABEL(BTN_JOYSTICK),
- LABEL(BTN_TRIGGER),
- LABEL(BTN_THUMB),
- LABEL(BTN_THUMB2),
- LABEL(BTN_TOP),
- LABEL(BTN_TOP2),
- LABEL(BTN_PINKIE),
- LABEL(BTN_BASE),
- LABEL(BTN_BASE2),
- LABEL(BTN_BASE3),
- LABEL(BTN_BASE4),
- LABEL(BTN_BASE5),
- LABEL(BTN_BASE6),
- LABEL(BTN_DEAD),
- LABEL(BTN_A),
- LABEL(BTN_B),
- LABEL(BTN_C),
- LABEL(BTN_X),
- LABEL(BTN_Y),
- LABEL(BTN_Z),
- LABEL(BTN_TL),
- LABEL(BTN_TR),
- LABEL(BTN_TL2),
- LABEL(BTN_TR2),
- LABEL(BTN_SELECT),
- LABEL(BTN_START),
- LABEL(BTN_MODE),
- LABEL(BTN_THUMBL),
- LABEL(BTN_THUMBR),
- LABEL(BTN_TOOL_PEN),
- LABEL(BTN_TOOL_RUBBER),
- LABEL(BTN_TOOL_BRUSH),
- LABEL(BTN_TOOL_PENCIL),
- LABEL(BTN_TOOL_AIRBRUSH),
- LABEL(BTN_TOOL_FINGER),
- LABEL(BTN_TOOL_MOUSE),
- LABEL(BTN_TOOL_LENS),
- LABEL(BTN_TOUCH),
- LABEL(BTN_STYLUS),
- LABEL(BTN_STYLUS2),
- LABEL(BTN_TOOL_DOUBLETAP),
- LABEL(BTN_TOOL_TRIPLETAP),
- LABEL(BTN_TOOL_QUADTAP),
- LABEL(BTN_GEAR_DOWN),
- LABEL(BTN_GEAR_UP),
- LABEL(KEY_OK),
- LABEL(KEY_SELECT),
- LABEL(KEY_GOTO),
- LABEL(KEY_CLEAR),
- LABEL(KEY_POWER2),
- LABEL(KEY_OPTION),
- LABEL(KEY_INFO),
- LABEL(KEY_TIME),
- LABEL(KEY_VENDOR),
- LABEL(KEY_ARCHIVE),
- LABEL(KEY_PROGRAM),
- LABEL(KEY_CHANNEL),
- LABEL(KEY_FAVORITES),
- LABEL(KEY_EPG),
- LABEL(KEY_PVR),
- LABEL(KEY_MHP),
- LABEL(KEY_LANGUAGE),
- LABEL(KEY_TITLE),
- LABEL(KEY_SUBTITLE),
- LABEL(KEY_ANGLE),
- LABEL(KEY_ZOOM),
- LABEL(KEY_MODE),
- LABEL(KEY_KEYBOARD),
- LABEL(KEY_SCREEN),
- LABEL(KEY_PC),
- LABEL(KEY_TV),
- LABEL(KEY_TV2),
- LABEL(KEY_VCR),
- LABEL(KEY_VCR2),
- LABEL(KEY_SAT),
- LABEL(KEY_SAT2),
- LABEL(KEY_CD),
- LABEL(KEY_TAPE),
- LABEL(KEY_RADIO),
- LABEL(KEY_TUNER),
- LABEL(KEY_PLAYER),
- LABEL(KEY_TEXT),
- LABEL(KEY_DVD),
- LABEL(KEY_AUX),
- LABEL(KEY_MP3),
- LABEL(KEY_AUDIO),
- LABEL(KEY_VIDEO),
- LABEL(KEY_DIRECTORY),
- LABEL(KEY_LIST),
- LABEL(KEY_MEMO),
- LABEL(KEY_CALENDAR),
- LABEL(KEY_RED),
- LABEL(KEY_GREEN),
- LABEL(KEY_YELLOW),
- LABEL(KEY_BLUE),
- LABEL(KEY_CHANNELUP),
- LABEL(KEY_CHANNELDOWN),
- LABEL(KEY_FIRST),
- LABEL(KEY_LAST),
- LABEL(KEY_AB),
- LABEL(KEY_NEXT),
- LABEL(KEY_RESTART),
- LABEL(KEY_SLOW),
- LABEL(KEY_SHUFFLE),
- LABEL(KEY_BREAK),
- LABEL(KEY_PREVIOUS),
- LABEL(KEY_DIGITS),
- LABEL(KEY_TEEN),
- LABEL(KEY_TWEN),
- LABEL(KEY_VIDEOPHONE),
- LABEL(KEY_GAMES),
- LABEL(KEY_ZOOMIN),
- LABEL(KEY_ZOOMOUT),
- LABEL(KEY_ZOOMRESET),
- LABEL(KEY_WORDPROCESSOR),
- LABEL(KEY_EDITOR),
- LABEL(KEY_SPREADSHEET),
- LABEL(KEY_GRAPHICSEDITOR),
- LABEL(KEY_PRESENTATION),
- LABEL(KEY_DATABASE),
- LABEL(KEY_NEWS),
- LABEL(KEY_VOICEMAIL),
- LABEL(KEY_ADDRESSBOOK),
- LABEL(KEY_MESSENGER),
- LABEL(KEY_DISPLAYTOGGLE),
- LABEL(KEY_SPELLCHECK),
- LABEL(KEY_LOGOFF),
- LABEL(KEY_DOLLAR),
- LABEL(KEY_EURO),
- LABEL(KEY_FRAMEBACK),
- LABEL(KEY_FRAMEFORWARD),
- LABEL(KEY_CONTEXT_MENU),
- LABEL(KEY_MEDIA_REPEAT),
- LABEL(KEY_10CHANNELSUP),
- LABEL(KEY_10CHANNELSDOWN),
- LABEL(KEY_IMAGES),
- LABEL(KEY_DEL_EOL),
- LABEL(KEY_DEL_EOS),
- LABEL(KEY_INS_LINE),
- LABEL(KEY_DEL_LINE),
- LABEL(KEY_FN),
- LABEL(KEY_FN_ESC),
- LABEL(KEY_FN_F1),
- LABEL(KEY_FN_F2),
- LABEL(KEY_FN_F3),
- LABEL(KEY_FN_F4),
- LABEL(KEY_FN_F5),
- LABEL(KEY_FN_F6),
- LABEL(KEY_FN_F7),
- LABEL(KEY_FN_F8),
- LABEL(KEY_FN_F9),
- LABEL(KEY_FN_F10),
- LABEL(KEY_FN_F11),
- LABEL(KEY_FN_F12),
- LABEL(KEY_FN_1),
- LABEL(KEY_FN_2),
- LABEL(KEY_FN_D),
- LABEL(KEY_FN_E),
- LABEL(KEY_FN_F),
- LABEL(KEY_FN_S),
- LABEL(KEY_FN_B),
- LABEL(KEY_BRL_DOT1),
- LABEL(KEY_BRL_DOT2),
- LABEL(KEY_BRL_DOT3),
- LABEL(KEY_BRL_DOT4),
- LABEL(KEY_BRL_DOT5),
- LABEL(KEY_BRL_DOT6),
- LABEL(KEY_BRL_DOT7),
- LABEL(KEY_BRL_DOT8),
- LABEL(KEY_BRL_DOT9),
- LABEL(KEY_BRL_DOT10),
- LABEL(KEY_NUMERIC_0),
- LABEL(KEY_NUMERIC_1),
- LABEL(KEY_NUMERIC_2),
- LABEL(KEY_NUMERIC_3),
- LABEL(KEY_NUMERIC_4),
- LABEL(KEY_NUMERIC_5),
- LABEL(KEY_NUMERIC_6),
- LABEL(KEY_NUMERIC_7),
- LABEL(KEY_NUMERIC_8),
- LABEL(KEY_NUMERIC_9),
- LABEL(KEY_NUMERIC_STAR),
- LABEL(KEY_NUMERIC_POUND),
- LABEL(KEY_CAMERA_FOCUS),
- LABEL(KEY_WPS_BUTTON),
- LABEL(KEY_TOUCHPAD_TOGGLE),
- LABEL(KEY_TOUCHPAD_ON),
- LABEL(KEY_TOUCHPAD_OFF),
- LABEL(KEY_CAMERA_ZOOMIN),
- LABEL(KEY_CAMERA_ZOOMOUT),
- LABEL(KEY_CAMERA_UP),
- LABEL(KEY_CAMERA_DOWN),
- LABEL(KEY_CAMERA_LEFT),
- LABEL(KEY_CAMERA_RIGHT),
- LABEL(BTN_TRIGGER_HAPPY1),
- LABEL(BTN_TRIGGER_HAPPY2),
- LABEL(BTN_TRIGGER_HAPPY3),
- LABEL(BTN_TRIGGER_HAPPY4),
- LABEL(BTN_TRIGGER_HAPPY5),
- LABEL(BTN_TRIGGER_HAPPY6),
- LABEL(BTN_TRIGGER_HAPPY7),
- LABEL(BTN_TRIGGER_HAPPY8),
- LABEL(BTN_TRIGGER_HAPPY9),
- LABEL(BTN_TRIGGER_HAPPY10),
- LABEL(BTN_TRIGGER_HAPPY11),
- LABEL(BTN_TRIGGER_HAPPY12),
- LABEL(BTN_TRIGGER_HAPPY13),
- LABEL(BTN_TRIGGER_HAPPY14),
- LABEL(BTN_TRIGGER_HAPPY15),
- LABEL(BTN_TRIGGER_HAPPY16),
- LABEL(BTN_TRIGGER_HAPPY17),
- LABEL(BTN_TRIGGER_HAPPY18),
- LABEL(BTN_TRIGGER_HAPPY19),
- LABEL(BTN_TRIGGER_HAPPY20),
- LABEL(BTN_TRIGGER_HAPPY21),
- LABEL(BTN_TRIGGER_HAPPY22),
- LABEL(BTN_TRIGGER_HAPPY23),
- LABEL(BTN_TRIGGER_HAPPY24),
- LABEL(BTN_TRIGGER_HAPPY25),
- LABEL(BTN_TRIGGER_HAPPY26),
- LABEL(BTN_TRIGGER_HAPPY27),
- LABEL(BTN_TRIGGER_HAPPY28),
- LABEL(BTN_TRIGGER_HAPPY29),
- LABEL(BTN_TRIGGER_HAPPY30),
- LABEL(BTN_TRIGGER_HAPPY31),
- LABEL(BTN_TRIGGER_HAPPY32),
- LABEL(BTN_TRIGGER_HAPPY33),
- LABEL(BTN_TRIGGER_HAPPY34),
- LABEL(BTN_TRIGGER_HAPPY35),
- LABEL(BTN_TRIGGER_HAPPY36),
- LABEL(BTN_TRIGGER_HAPPY37),
- LABEL(BTN_TRIGGER_HAPPY38),
- LABEL(BTN_TRIGGER_HAPPY39),
- LABEL(BTN_TRIGGER_HAPPY40),
- LABEL_END,
-};
-
-static struct label rel_labels[] = {
- LABEL(REL_X),
- LABEL(REL_Y),
- LABEL(REL_Z),
- LABEL(REL_RX),
- LABEL(REL_RY),
- LABEL(REL_RZ),
- LABEL(REL_HWHEEL),
- LABEL(REL_DIAL),
- LABEL(REL_WHEEL),
- LABEL(REL_MISC),
- LABEL_END,
-};
-
-static struct label abs_labels[] = {
- LABEL(ABS_X),
- LABEL(ABS_Y),
- LABEL(ABS_Z),
- LABEL(ABS_RX),
- LABEL(ABS_RY),
- LABEL(ABS_RZ),
- LABEL(ABS_THROTTLE),
- LABEL(ABS_RUDDER),
- LABEL(ABS_WHEEL),
- LABEL(ABS_GAS),
- LABEL(ABS_BRAKE),
- LABEL(ABS_HAT0X),
- LABEL(ABS_HAT0Y),
- LABEL(ABS_HAT1X),
- LABEL(ABS_HAT1Y),
- LABEL(ABS_HAT2X),
- LABEL(ABS_HAT2Y),
- LABEL(ABS_HAT3X),
- LABEL(ABS_HAT3Y),
- LABEL(ABS_PRESSURE),
- LABEL(ABS_DISTANCE),
- LABEL(ABS_TILT_X),
- LABEL(ABS_TILT_Y),
- LABEL(ABS_TOOL_WIDTH),
- LABEL(ABS_VOLUME),
- LABEL(ABS_MISC),
- LABEL(ABS_MT_SLOT),
- LABEL(ABS_MT_TOUCH_MAJOR),
- LABEL(ABS_MT_TOUCH_MINOR),
- LABEL(ABS_MT_WIDTH_MAJOR),
- LABEL(ABS_MT_WIDTH_MINOR),
- LABEL(ABS_MT_ORIENTATION),
- LABEL(ABS_MT_POSITION_X),
- LABEL(ABS_MT_POSITION_Y),
- LABEL(ABS_MT_TOOL_TYPE),
- LABEL(ABS_MT_BLOB_ID),
- LABEL(ABS_MT_TRACKING_ID),
- LABEL(ABS_MT_PRESSURE),
- LABEL(ABS_MT_DISTANCE),
- LABEL_END,
-};
-
-static struct label sw_labels[] = {
- LABEL(SW_LID),
- LABEL(SW_TABLET_MODE),
- LABEL(SW_HEADPHONE_INSERT),
- LABEL(SW_RFKILL_ALL),
- LABEL(SW_RADIO),
- LABEL(SW_MICROPHONE_INSERT),
- LABEL(SW_DOCK),
- LABEL(SW_LINEOUT_INSERT),
- LABEL(SW_JACK_PHYSICAL_INSERT),
- LABEL(SW_VIDEOOUT_INSERT),
- LABEL(SW_CAMERA_LENS_COVER),
- LABEL(SW_KEYPAD_SLIDE),
- LABEL(SW_FRONT_PROXIMITY),
- LABEL(SW_ROTATE_LOCK),
- LABEL_END,
-};
-
-static struct label msc_labels[] = {
- LABEL(MSC_SERIAL),
- LABEL(MSC_PULSELED),
- LABEL(MSC_GESTURE),
- LABEL(MSC_RAW),
- LABEL(MSC_SCAN),
- LABEL_END,
-};
-
-static struct label led_labels[] = {
- LABEL(LED_NUML),
- LABEL(LED_CAPSL),
- LABEL(LED_SCROLLL),
- LABEL(LED_COMPOSE),
- LABEL(LED_KANA),
- LABEL(LED_SLEEP),
- LABEL(LED_SUSPEND),
- LABEL(LED_MUTE),
- LABEL(LED_MISC),
- LABEL(LED_MAIL),
- LABEL(LED_CHARGING),
- LABEL_END,
-};
-
-static struct label rep_labels[] = {
- LABEL(REP_DELAY),
- LABEL(REP_PERIOD),
- LABEL_END,
-};
-
-static struct label snd_labels[] = {
- LABEL(SND_CLICK),
- LABEL(SND_BELL),
- LABEL(SND_TONE),
- LABEL_END,
-};
-
-#if 0
-static struct label id_labels[] = {
- LABEL(ID_BUS),
- LABEL(ID_VENDOR),
- LABEL(ID_PRODUCT),
- LABEL(ID_VERSION),
- LABEL_END,
-};
-
-static struct label bus_labels[] = {
- LABEL(BUS_PCI),
- LABEL(BUS_ISAPNP),
- LABEL(BUS_USB),
- LABEL(BUS_HIL),
- LABEL(BUS_BLUETOOTH),
- LABEL(BUS_VIRTUAL),
- LABEL(BUS_ISA),
- LABEL(BUS_I8042),
- LABEL(BUS_XTKBD),
- LABEL(BUS_RS232),
- LABEL(BUS_GAMEPORT),
- LABEL(BUS_PARPORT),
- LABEL(BUS_AMIGA),
- LABEL(BUS_ADB),
- LABEL(BUS_I2C),
- LABEL(BUS_HOST),
- LABEL(BUS_GSC),
- LABEL(BUS_ATARI),
- LABEL(BUS_SPI),
- LABEL_END,
-};
-#endif
-
-static struct label mt_tool_labels[] = {
- LABEL(MT_TOOL_FINGER),
- LABEL(MT_TOOL_PEN),
- LABEL(MT_TOOL_MAX),
- LABEL_END,
-};
-
-static struct label ff_status_labels[] = {
- LABEL(FF_STATUS_STOPPED),
- LABEL(FF_STATUS_PLAYING),
- LABEL(FF_STATUS_MAX),
- LABEL_END,
-};
-
-static struct label ff_labels[] = {
- LABEL(FF_RUMBLE),
- LABEL(FF_PERIODIC),
- LABEL(FF_CONSTANT),
- LABEL(FF_SPRING),
- LABEL(FF_FRICTION),
- LABEL(FF_DAMPER),
- LABEL(FF_INERTIA),
- LABEL(FF_RAMP),
- LABEL(FF_SQUARE),
- LABEL(FF_TRIANGLE),
- LABEL(FF_SINE),
- LABEL(FF_SAW_UP),
- LABEL(FF_SAW_DOWN),
- LABEL(FF_CUSTOM),
- LABEL(FF_GAIN),
- LABEL(FF_AUTOCENTER),
- LABEL_END,
-};
-
-static struct label key_value_labels[] = {
- { "UP", 0 },
- { "DOWN", 1 },
- { "REPEAT", 2 },
- LABEL_END,
-};
diff --git a/toolbox/getprop.c b/toolbox/getprop.c
deleted file mode 100644
index dcc0ea0..0000000
--- a/toolbox/getprop.c
+++ /dev/null
@@ -1,50 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <cutils/properties.h>
-
-#include "dynarray.h"
-
-static void record_prop(const char* key, const char* name, void* opaque)
-{
- strlist_t* list = opaque;
- char temp[PROP_VALUE_MAX + PROP_NAME_MAX + 16];
- snprintf(temp, sizeof temp, "[%s]: [%s]", key, name);
- strlist_append_dup(list, temp);
-}
-
-static void list_properties(void)
-{
- strlist_t list[1] = { STRLIST_INITIALIZER };
-
- /* Record properties in the string list */
- (void)property_list(record_prop, list);
-
- /* Sort everything */
- strlist_sort(list);
-
- /* print everything */
- STRLIST_FOREACH(list, str, printf("%s\n", str));
-
- /* voila */
- strlist_done(list);
-}
-
-int getprop_main(int argc, char *argv[])
-{
- if (argc == 1) {
- list_properties();
- } else {
- char value[PROPERTY_VALUE_MAX];
- char *default_value;
- if(argc > 2) {
- default_value = argv[2];
- } else {
- default_value = "";
- }
-
- property_get(argv[1], value, default_value);
- printf("%s\n", value);
- }
- return 0;
-}
diff --git a/toolbox/ioctl.c b/toolbox/ioctl.c
index d1cc14a..093e467 100644
--- a/toolbox/ioctl.c
+++ b/toolbox/ioctl.c
@@ -1,36 +1,81 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <error.h>
#include <fcntl.h>
#include <getopt.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
#include <string.h>
-#include <linux/kd.h>
-#include <linux/vt.h>
-#include <errno.h>
-#include <pthread.h>
#include <sys/ioctl.h>
#include <unistd.h>
-int ioctl_main(int argc, char *argv[])
-{
- int c;
- int fd;
- int res;
+static void usage() {
+ fprintf(stderr, "%s [-l <length>] [-a <argsize>] [-rdh] <device> <ioctlnr>\n"
+ " -l <length> Length of io buffer\n"
+ " -a <argsize> Size of each argument (1-8)\n"
+ " -r Open device in read only mode\n"
+ " -d Direct argument (no iobuffer)\n"
+ " -h Print help\n", getprogname());
+ exit(1);
+}
+static int xstrtoi(const char* s, const char* what) {
+ char* endp;
+ errno = 0;
+ long result = strtol(s, &endp, 0);
+ if (errno != 0 || *endp != '\0') {
+ error(1, errno, "couldn't parse %s '%s'", what, s);
+ }
+ if (result > INT_MAX || result < INT_MIN) {
+ error(1, errno, "%s '%s' out of range", what, s);
+ }
+ return result;
+}
+
+int ioctl_main(int argc, char* argv[]) {
int read_only = 0;
int length = -1;
int arg_size = 4;
int direct_arg = 0;
- uint32_t ioctl_nr;
+
void *ioctl_args = NULL;
uint8_t *ioctl_argp;
uint8_t *ioctl_argp_save = NULL;
int rem;
- do {
- c = getopt(argc, argv, "rdl:a:h");
- if (c == EOF)
- break;
+ int c;
+ while ((c = getopt(argc, argv, "rdl:a:h")) != -1) {
switch (c) {
case 'r':
read_only = 1;
@@ -39,43 +84,44 @@
direct_arg = 1;
break;
case 'l':
- length = strtol(optarg, NULL, 0);
+ length = xstrtoi(optarg, "length");
break;
case 'a':
- arg_size = strtol(optarg, NULL, 0);
+ arg_size = xstrtoi(optarg, "argument size");
break;
case 'h':
- fprintf(stderr, "%s [-l <length>] [-a <argsize>] [-rdh] <device> <ioctlnr>\n"
- " -l <length> Length of io buffer\n"
- " -a <argsize> Size of each argument (1-8)\n"
- " -r Open device in read only mode\n"
- " -d Direct argument (no iobuffer)\n"
- " -h Print help\n", argv[0]);
- return -1;
- case '?':
- fprintf(stderr, "%s: invalid option -%c\n",
- argv[0], optopt);
- exit(1);
+ usage();
+ break;
+ default:
+ error(1, 0, "invalid option -%c", optopt);
}
- } while (1);
-
- if(optind + 2 > argc) {
- fprintf(stderr, "%s: too few arguments\n", argv[0]);
- exit(1);
}
- if (!strcmp(argv[optind], "-")) {
+ if (optind + 2 > argc) {
+ usage();
+ }
+
+ const char* device = argv[optind];
+ int fd;
+ if (strcmp(device, "-") == 0) {
fd = STDIN_FILENO;
} else {
- fd = open(argv[optind], read_only ? O_RDONLY : (O_RDWR | O_SYNC));
- if (fd < 0) {
- fprintf(stderr, "cannot open %s\n", argv[optind]);
- return 1;
+ fd = open(device, read_only ? O_RDONLY : (O_RDWR | O_SYNC));
+ if (fd == -1) {
+ error(1, errno, "cannot open %s", argv[optind]);
}
}
optind++;
-
- ioctl_nr = strtol(argv[optind], NULL, 0);
+
+ // IOCTL(2) wants second parameter as a signed int.
+ // Let's let the user specify either negative numbers or large positive
+ // numbers, for the case where ioctl number is larger than INT_MAX.
+ errno = 0;
+ char* endp;
+ int ioctl_nr = UINT_MAX & strtoll(argv[optind], &endp, 0);
+ if (errno != 0 || *endp != '\0') {
+ error(1, errno, "couldn't parse ioctl number '%s'", argv[optind]);
+ }
optind++;
if(direct_arg) {
@@ -91,11 +137,10 @@
ioctl_argp_save = ioctl_argp = ioctl_args;
rem = length;
- while(optind < argc) {
+ while (optind < argc) {
uint64_t tmp = strtoull(argv[optind], NULL, 0);
- if(rem < arg_size) {
- fprintf(stderr, "%s: too many arguments\n", argv[0]);
- exit(1);
+ if (rem < arg_size) {
+ error(1, 0, "too many arguments");
}
memcpy(ioctl_argp, &tmp, arg_size);
ioctl_argp += arg_size;
@@ -108,8 +153,9 @@
while(rem--) {
printf(" 0x%02x", *ioctl_argp_save++);
}
- printf("\n");
+ printf(" to %s\n", device);
+ int res;
if(direct_arg)
res = ioctl(fd, ioctl_nr, *(uint32_t*)ioctl_args);
else if(length)
@@ -118,10 +164,10 @@
res = ioctl(fd, ioctl_nr, 0);
if (res < 0) {
free(ioctl_args);
- fprintf(stderr, "ioctl 0x%x failed, %d\n", ioctl_nr, res);
- return 1;
+ error(1, errno, "ioctl 0x%x failed (returned %d)", ioctl_nr, res);
}
- if(length) {
+
+ if (length) {
printf("return buf:");
ioctl_argp = ioctl_args;
rem = length;
@@ -131,5 +177,6 @@
printf("\n");
}
free(ioctl_args);
+ close(fd);
return 0;
}
diff --git a/toolbox/load_policy.c b/toolbox/load_policy.c
deleted file mode 100644
index 90d48c4..0000000
--- a/toolbox/load_policy.c
+++ /dev/null
@@ -1,49 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <errno.h>
-#include <selinux/selinux.h>
-
-int load_policy_main(int argc, char **argv)
-{
- int fd, rc;
- struct stat sb;
- void *map;
- const char *path;
-
- if (argc != 2) {
- fprintf(stderr, "usage: %s policy-file\n", argv[0]);
- exit(1);
- }
-
- path = argv[1];
- fd = open(path, O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "Could not open %s: %s\n", path, strerror(errno));
- exit(2);
- }
-
- if (fstat(fd, &sb) < 0) {
- fprintf(stderr, "Could not stat %s: %s\n", path, strerror(errno));
- exit(3);
- }
-
- map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
- if (map == MAP_FAILED) {
- fprintf(stderr, "Could not mmap %s: %s\n", path, strerror(errno));
- exit(4);
- }
-
- rc = security_load_policy(map, sb.st_size);
- if (rc < 0) {
- fprintf(stderr, "Could not load %s: %s\n", path, strerror(errno));
- exit(5);
- }
- munmap(map, sb.st_size);
- close(fd);
- exit(0);
-}
diff --git a/toolbox/ls.c b/toolbox/ls.c
index 963fcb5..9a89dd4 100644
--- a/toolbox/ls.c
+++ b/toolbox/ls.c
@@ -1,23 +1,119 @@
+#include <dirent.h>
+#include <errno.h>
+#include <grp.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
#include <sys/types.h>
-#include <dirent.h>
-#include <errno.h>
+#include <time.h>
+#include <unistd.h>
#include <selinux/selinux.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <time.h>
+// simple dynamic array of strings.
+typedef struct {
+ int count;
+ int capacity;
+ void** items;
+} strlist_t;
-#include <pwd.h>
-#include <grp.h>
+#define STRLIST_INITIALIZER { 0, 0, NULL }
-#include <linux/kdev_t.h>
-#include <limits.h>
+/* Used to iterate over a strlist_t
+ * _list :: pointer to strlist_t object
+ * _item :: name of local variable name defined within the loop with
+ * type 'char*'
+ * _stmnt :: C statement executed in each iteration
+ *
+ * This macro is only intended for simple uses. Do not add or remove items
+ * to/from the list during iteration.
+ */
+#define STRLIST_FOREACH(_list,_item,_stmnt) \
+ do { \
+ int _nn_##__LINE__ = 0; \
+ for (;_nn_##__LINE__ < (_list)->count; ++ _nn_##__LINE__) { \
+ char* _item = (char*)(_list)->items[_nn_##__LINE__]; \
+ _stmnt; \
+ } \
+ } while (0)
-#include "dynarray.h"
+static void dynarray_reserve_more( strlist_t *a, int count ) {
+ int old_cap = a->capacity;
+ int new_cap = old_cap;
+ const int max_cap = INT_MAX/sizeof(void*);
+ void** new_items;
+ int new_count = a->count + count;
+
+ if (count <= 0)
+ return;
+
+ if (count > max_cap - a->count)
+ abort();
+
+ new_count = a->count + count;
+
+ while (new_cap < new_count) {
+ old_cap = new_cap;
+ new_cap += (new_cap >> 2) + 4;
+ if (new_cap < old_cap || new_cap > max_cap) {
+ new_cap = max_cap;
+ }
+ }
+ new_items = realloc(a->items, new_cap*sizeof(void*));
+ if (new_items == NULL)
+ abort();
+
+ a->items = new_items;
+ a->capacity = new_cap;
+}
+
+void strlist_init( strlist_t *list ) {
+ list->count = list->capacity = 0;
+ list->items = NULL;
+}
+
+// append a new string made of the first 'slen' characters from 'str'
+// followed by a trailing zero.
+void strlist_append_b( strlist_t *list, const void* str, size_t slen ) {
+ char *copy = malloc(slen+1);
+ memcpy(copy, str, slen);
+ copy[slen] = '\0';
+ if (list->count >= list->capacity)
+ dynarray_reserve_more(list, 1);
+ list->items[list->count++] = copy;
+}
+
+// append the copy of a given input string to a strlist_t.
+void strlist_append_dup( strlist_t *list, const char *str) {
+ strlist_append_b(list, str, strlen(str));
+}
+
+// note: strlist_done will free all the strings owned by the list.
+void strlist_done( strlist_t *list ) {
+ STRLIST_FOREACH(list, string, free(string));
+ free(list->items);
+ list->items = NULL;
+ list->count = list->capacity = 0;
+}
+
+static int strlist_compare_strings(const void* a, const void* b) {
+ const char *sa = *(const char **)a;
+ const char *sb = *(const char **)b;
+ return strcmp(sa, sb);
+}
+
+/* sort the strings in a given list (using strcmp) */
+void strlist_sort( strlist_t *list ) {
+ if (list->count > 0) {
+ qsort(list->items, (size_t)list->count, sizeof(void*), strlist_compare_strings);
+ }
+}
+
// bits for flags argument
#define LIST_LONG (1 << 0)
@@ -200,7 +296,7 @@
case S_IFCHR:
printf("%s %-8s %-8s %3d, %3d %s %s\n",
mode, user, group,
- (int) MAJOR(s->st_rdev), (int) MINOR(s->st_rdev),
+ major(s->st_rdev), minor(s->st_rdev),
date, name);
break;
case S_IFREG:
diff --git a/toolbox/newfs_msdos.c b/toolbox/newfs_msdos.c
index 01517fd..5b98a01 100644
--- a/toolbox/newfs_msdos.c
+++ b/toolbox/newfs_msdos.c
@@ -798,6 +798,7 @@
__unused int oflag,struct bpb *bpb)
{
struct hd_geometry geom;
+ u_long block_size;
if (ioctl(fd, BLKSSZGET, &bpb->bps)) {
fprintf(stderr, "Error getting bytes / sector (%s)\n", strerror(errno));
@@ -806,11 +807,18 @@
ckgeom(fname, bpb->bps, "bytes/sector");
- if (ioctl(fd, BLKGETSIZE, &bpb->bsec)) {
+ if (ioctl(fd, BLKGETSIZE, &block_size)) {
fprintf(stderr, "Error getting blocksize (%s)\n", strerror(errno));
exit(1);
}
+ if (block_size > UINT32_MAX) {
+ fprintf(stderr, "Error blocksize too large: %lu\n", block_size);
+ exit(1);
+ }
+
+ bpb->bsec = (u_int)block_size;
+
if (ioctl(fd, HDIO_GETGEO, &geom)) {
fprintf(stderr, "Error getting gemoetry (%s) - trying sane values\n", strerror(errno));
geom.heads = 64;
diff --git a/toolbox/restorecon.c b/toolbox/restorecon.c
deleted file mode 100644
index cb5799e..0000000
--- a/toolbox/restorecon.c
+++ /dev/null
@@ -1,63 +0,0 @@
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <selinux/selinux.h>
-#include <selinux/android.h>
-
-static const char *progname;
-
-static void usage(void)
-{
- fprintf(stderr, "usage: %s [-DFnrRv] pathname...\n", progname);
- exit(1);
-}
-
-int restorecon_main(int argc, char **argv)
-{
- int ch, i, rc;
- unsigned int flags = 0;
-
- progname = argv[0];
-
- do {
- ch = getopt(argc, argv, "DFnrRv");
- if (ch == EOF)
- break;
- switch (ch) {
- case 'D':
- flags |= SELINUX_ANDROID_RESTORECON_DATADATA;
- break;
- case 'F':
- flags |= SELINUX_ANDROID_RESTORECON_FORCE;
- break;
- case 'n':
- flags |= SELINUX_ANDROID_RESTORECON_NOCHANGE;
- break;
- case 'r':
- case 'R':
- flags |= SELINUX_ANDROID_RESTORECON_RECURSE;
- break;
- case 'v':
- flags |= SELINUX_ANDROID_RESTORECON_VERBOSE;
- break;
- default:
- usage();
- }
- } while (1);
-
- argc -= optind;
- argv += optind;
- if (!argc)
- usage();
-
- for (i = 0; i < argc; i++) {
- rc = selinux_android_restorecon(argv[i], flags);
- if (rc < 0)
- fprintf(stderr, "Could not restorecon %s: %s\n", argv[i],
- strerror(errno));
- }
-
- return 0;
-}
diff --git a/toolbox/route.c b/toolbox/route.c
deleted file mode 100644
index 3e10014..0000000
--- a/toolbox/route.c
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (c) 2009, The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <linux/route.h>
-
-static inline int set_address(const char *address, struct sockaddr *sa) {
- return inet_aton(address, &((struct sockaddr_in *)sa)->sin_addr);
-}
-
-/* current support the following routing entries */
-/* route add default dev wlan0 */
-/* route add default gw 192.168.1.1 dev wlan0 */
-/* route add -net 192.168.1.2 netmask 255.255.255.0 gw 192.168.1.1 */
-
-int route_main(int argc, char *argv[])
-{
- struct rtentry rt = {
- .rt_dst = {.sa_family = AF_INET},
- .rt_genmask = {.sa_family = AF_INET},
- .rt_gateway = {.sa_family = AF_INET},
- };
-
- errno = EINVAL;
- if (argc > 2 && !strcmp(argv[1], "add")) {
- if (!strcmp(argv[2], "default")) {
- /* route add default dev wlan0 */
- if (argc > 4 && !strcmp(argv[3], "dev")) {
- rt.rt_flags = RTF_UP;
- rt.rt_dev = argv[4];
- errno = 0;
- goto apply;
- }
-
- /* route add default gw 192.168.1.1 dev wlan0 */
- if (argc > 6 && !strcmp(argv[3], "gw") && !strcmp(argv[5], "dev")) {
- rt.rt_flags = RTF_UP | RTF_GATEWAY;
- rt.rt_dev = argv[6];
- if (set_address(argv[4], &rt.rt_gateway)) {
- errno = 0;
- }
- goto apply;
- }
- }
-
- /* route add -net 192.168.1.2 netmask 255.255.255.0 gw 192.168.1.1 */
- if (argc > 7 && !strcmp(argv[2], "-net") &&
- !strcmp(argv[4], "netmask")) {
- if (!strcmp(argv[6], "gw")) {
- rt.rt_flags = RTF_UP | RTF_GATEWAY;
- if (set_address(argv[3], &rt.rt_dst) &&
- set_address(argv[5], &rt.rt_genmask) &&
- set_address(argv[7], &rt.rt_gateway)) {
- errno = 0;
- }
- goto apply;
- } else if (!strcmp(argv[6], "dev")) {
- rt.rt_flags = RTF_UP;
- rt.rt_dev = argv[7];
- if (set_address(argv[3], &rt.rt_dst) &&
- set_address(argv[5], &rt.rt_genmask)) {
- errno = 0;
- }
- goto apply;
- }
- }
- }
-
-apply:
- if (!errno) {
- int s = socket(AF_INET, SOCK_DGRAM, 0);
- if (s != -1 && (ioctl(s, SIOCADDRT, &rt) != -1 || errno == EEXIST)) {
- return 0;
- }
- }
- puts(strerror(errno));
- return errno;
-}
diff --git a/toolbox/runcon.c b/toolbox/runcon.c
deleted file mode 100644
index 4a57bf3..0000000
--- a/toolbox/runcon.c
+++ /dev/null
@@ -1,28 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-#include <selinux/selinux.h>
-
-int runcon_main(int argc, char **argv)
-{
- int rc;
-
- if (argc < 3) {
- fprintf(stderr, "usage: %s context program args...\n", argv[0]);
- exit(1);
- }
-
- rc = setexeccon(argv[1]);
- if (rc < 0) {
- fprintf(stderr, "Could not set context to %s: %s\n", argv[1], strerror(errno));
- exit(2);
- }
-
- argv += 2;
- argc -= 2;
- execvp(argv[0], argv);
- fprintf(stderr, "Could not exec %s: %s\n", argv[0], strerror(errno));
- exit(3);
-}
diff --git a/toolbox/schedtop.c b/toolbox/schedtop.c
deleted file mode 100644
index 2fccd2e..0000000
--- a/toolbox/schedtop.c
+++ /dev/null
@@ -1,330 +0,0 @@
-#include <ctype.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-struct thread_info {
- int pid;
- int tid;
- char name[64];
- unsigned long long exec_time;
- unsigned long long delay_time;
- unsigned long long run_count;
-};
-
-struct thread_table {
- size_t allocated;
- size_t active;
- struct thread_info *data;
-};
-
-enum {
- FLAG_BATCH = 1U << 0,
- FLAG_HIDE_IDLE = 1U << 1,
- FLAG_SHOW_THREADS = 1U << 2,
- FLAG_USE_ALTERNATE_SCREEN = 1U << 3,
-};
-
-static int time_dp = 9;
-static int time_div = 1;
-#define NS_TO_S_D(ns) \
- (uint32_t)((ns) / 1000000000), time_dp, ((uint32_t)((ns) % 1000000000) / time_div)
-
-struct thread_table processes;
-struct thread_table last_processes;
-struct thread_table threads;
-struct thread_table last_threads;
-
-static void grow_table(struct thread_table *table)
-{
- size_t size = table->allocated;
- struct thread_info *new_table;
- if (size < 128)
- size = 128;
- else
- size *= 2;
-
- new_table = realloc(table->data, size * sizeof(*table->data));
- if (new_table == NULL) {
- fprintf(stderr, "out of memory\n");
- exit(1);
- }
- table->data = new_table;
- table->allocated = size;
-}
-
-static struct thread_info *get_item(struct thread_table *table)
-{
- if (table->active >= table->allocated)
- grow_table(table);
- return table->data + table->active;
-}
-
-static void commit_item(struct thread_table *table)
-{
- table->active++;
-}
-
-static int read_line(char *line, size_t line_size)
-{
- int fd;
- int len;
- fd = open(line, O_RDONLY);
- if(fd == 0)
- return -1;
- len = read(fd, line, line_size - 1);
- close(fd);
- if (len <= 0)
- return -1;
- line[len] = '\0';
- return 0;
-}
-
-static void add_thread(int pid, int tid, struct thread_info *proc_info)
-{
- char line[1024];
- char *name, *name_end;
- size_t name_len;
- struct thread_info *info;
- if(tid == 0)
- info = get_item(&processes);
- else
- info = get_item(&threads);
- info->pid = pid;
- info->tid = tid;
-
- if(tid)
- sprintf(line, "/proc/%d/task/%d/schedstat", pid, tid);
- else
- sprintf(line, "/proc/%d/schedstat", pid);
- if (read_line(line, sizeof(line)))
- return;
- if(sscanf(line, "%llu %llu %llu",
- &info->exec_time, &info->delay_time, &info->run_count) != 3)
- return;
- if (proc_info) {
- proc_info->exec_time += info->exec_time;
- proc_info->delay_time += info->delay_time;
- proc_info->run_count += info->run_count;
- }
-
- name = NULL;
- if (!tid) {
- sprintf(line, "/proc/%d/cmdline", pid);
- if (read_line(line, sizeof(line)) == 0 && line[0]) {
- name = line;
- name_len = strlen(name);
- }
- }
- if (!name) {
- if (tid)
- sprintf(line, "/proc/%d/task/%d/stat", pid, tid);
- else
- sprintf(line, "/proc/%d/stat", pid);
- if (read_line(line, sizeof(line)))
- return;
- name = strchr(line, '(');
- if (name == NULL)
- return;
- name_end = strchr(name, ')');
- if (name_end == NULL)
- return;
- name++;
- name_len = name_end - name;
- }
- if (name_len >= sizeof(info->name))
- name_len = sizeof(info->name) - 1;
- memcpy(info->name, name, name_len);
- info->name[name_len] = '\0';
- if(tid == 0)
- commit_item(&processes);
- else
- commit_item(&threads);
-}
-
-static void add_threads(int pid, struct thread_info *proc_info)
-{
- char path[1024];
- DIR *d;
- struct dirent *de;
- sprintf(path, "/proc/%d/task", pid);
- d = opendir(path);
- if(d == 0) return;
- while((de = readdir(d)) != 0){
- if(isdigit(de->d_name[0])){
- int tid = atoi(de->d_name);
- add_thread(pid, tid, proc_info);
- }
- }
- closedir(d);
-}
-
-static void print_threads(int pid, uint32_t flags)
-{
- size_t i, j;
- for (i = 0; i < last_threads.active; i++) {
- int epid = last_threads.data[i].pid;
- int tid = last_threads.data[i].tid;
- if (epid != pid)
- continue;
- for (j = 0; j < threads.active; j++)
- if (tid == threads.data[j].tid)
- break;
- if (j == threads.active)
- printf(" %5u died\n", tid);
- else if (!(flags & FLAG_HIDE_IDLE) || threads.data[j].run_count - last_threads.data[i].run_count)
- printf(" %5u %2u.%0*u %2u.%0*u %5llu %5u.%0*u %5u.%0*u %7llu %s\n", tid,
- NS_TO_S_D(threads.data[j].exec_time - last_threads.data[i].exec_time),
- NS_TO_S_D(threads.data[j].delay_time - last_threads.data[i].delay_time),
- threads.data[j].run_count - last_threads.data[i].run_count,
- NS_TO_S_D(threads.data[j].exec_time), NS_TO_S_D(threads.data[j].delay_time),
- threads.data[j].run_count, threads.data[j].name);
- }
-}
-
-static void update_table(DIR *d, uint32_t flags)
-{
- size_t i, j;
- struct dirent *de;
-
- rewinddir(d);
- while((de = readdir(d)) != 0){
- if(isdigit(de->d_name[0])){
- int pid = atoi(de->d_name);
- struct thread_info *proc_info;
- add_thread(pid, 0, NULL);
- proc_info = &processes.data[processes.active - 1];
- proc_info->exec_time = 0;
- proc_info->delay_time = 0;
- proc_info->run_count = 0;
- add_threads(pid, proc_info);
- }
- }
- if (!(flags & FLAG_BATCH))
- printf("\e[H\e[0J");
- printf("Processes: %zu, Threads %zu\n", processes.active, threads.active);
- switch (time_dp) {
- case 3:
- printf(" TID --- SINCE LAST ---- ---------- TOTAL ----------\n");
- printf(" PID EXEC_T DELAY SCHED EXEC_TIME DELAY_TIM SCHED NAME\n");
- break;
- case 6:
- printf(" TID ------ SINCE LAST ------- ------------ TOTAL -----------\n");
- printf(" PID EXEC_TIME DELAY_TIM SCHED EXEC_TIME DELAY_TIME SCHED NAME\n");
- break;
- default:
- printf(" TID -------- SINCE LAST -------- ------------- TOTAL -------------\n");
- printf(" PID EXEC_TIME DELAY_TIME SCHED EXEC_TIME DELAY_TIME SCHED NAME\n");
- break;
- }
- for (i = 0; i < last_processes.active; i++) {
- int pid = last_processes.data[i].pid;
- for (j = 0; j < processes.active; j++)
- if (pid == processes.data[j].pid)
- break;
- if (j == processes.active)
- printf("%5u died\n", pid);
- else if (!(flags & FLAG_HIDE_IDLE) || processes.data[j].run_count - last_processes.data[i].run_count) {
- printf("%5u %2u.%0*u %2u.%0*u %5llu %5u.%0*u %5u.%0*u %7llu %s\n", pid,
- NS_TO_S_D(processes.data[j].exec_time - last_processes.data[i].exec_time),
- NS_TO_S_D(processes.data[j].delay_time - last_processes.data[i].delay_time),
- processes.data[j].run_count - last_processes.data[i].run_count,
- NS_TO_S_D(processes.data[j].exec_time), NS_TO_S_D(processes.data[j].delay_time),
- processes.data[j].run_count, processes.data[j].name);
- if (flags & FLAG_SHOW_THREADS)
- print_threads(pid, flags);
- }
- }
-
- {
- struct thread_table tmp;
- tmp = last_processes;
- last_processes = processes;
- processes = tmp;
- processes.active = 0;
- tmp = last_threads;
- last_threads = threads;
- threads = tmp;
- threads.active = 0;
- }
-}
-
-void
-sig_abort(int signum)
-{
- printf("\e[?47l");
- exit(0);
-}
-
-
-int schedtop_main(int argc, char **argv)
-{
- int c;
- DIR *d;
- uint32_t flags = 0;
- int delay = 3000000;
- float delay_f;
-
- while(1) {
- c = getopt(argc, argv, "d:ibtamun");
- if (c == EOF)
- break;
- switch (c) {
- case 'd':
- delay_f = atof(optarg);
- delay = delay_f * 1000000;
- break;
- case 'b':
- flags |= FLAG_BATCH;
- break;
- case 'i':
- flags |= FLAG_HIDE_IDLE;
- break;
- case 't':
- flags |= FLAG_SHOW_THREADS;
- break;
- case 'a':
- flags |= FLAG_USE_ALTERNATE_SCREEN;
- break;
- case 'm':
- time_dp = 3;
- time_div = 1000000;
- break;
- case 'u':
- time_dp = 6;
- time_div = 1000;
- break;
- case 'n':
- time_dp = 9;
- time_div = 1;
- break;
- }
- }
-
- d = opendir("/proc");
- if(d == 0) return -1;
-
- if (!(flags & FLAG_BATCH)) {
- if(flags & FLAG_USE_ALTERNATE_SCREEN) {
- signal(SIGINT, sig_abort);
- signal(SIGPIPE, sig_abort);
- signal(SIGTERM, sig_abort);
- printf("\e7\e[?47h");
- }
- printf("\e[2J");
- }
- while (1) {
- update_table(d, flags);
- usleep(delay);
- }
- closedir(d);
- return 0;
-}
diff --git a/toolbox/setprop.c b/toolbox/setprop.c
deleted file mode 100644
index 63ad2b4..0000000
--- a/toolbox/setprop.c
+++ /dev/null
@@ -1,18 +0,0 @@
-#include <stdio.h>
-
-#include <cutils/properties.h>
-
-int setprop_main(int argc, char *argv[])
-{
- if(argc != 3) {
- fprintf(stderr,"usage: setprop <key> <value>\n");
- return 1;
- }
-
- if(property_set(argv[1], argv[2])){
- fprintf(stderr,"could not set property\n");
- return 1;
- }
-
- return 0;
-}
diff --git a/toolbox/toolbox.c b/toolbox/toolbox.c
index 0eac390..915da44 100644
--- a/toolbox/toolbox.c
+++ b/toolbox/toolbox.c
@@ -1,6 +1,8 @@
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
int main(int, char **);
@@ -31,11 +33,24 @@
{ 0, 0 },
};
+static void SIGPIPE_handler(int signal) {
+ // Those desktop Linux tools that catch SIGPIPE seem to agree that it's
+ // a successful way to exit, not a failure. (Which makes sense --- we were
+ // told to stop by a reader, rather than failing to continue ourselves.)
+ _exit(0);
+}
+
int main(int argc, char **argv)
{
int i;
char *name = argv[0];
+ // Let's assume that none of this code handles broken pipes. At least ls,
+ // ps, and top were broken (though I'd previously added this fix locally
+ // to top). We exit rather than use SIG_IGN because tools like top will
+ // just keep on writing to nowhere forever if we don't stop them.
+ signal(SIGPIPE, SIGPIPE_handler);
+
if((argc > 1) && (argv[1][0] == '@')) {
name = argv[1] + 1;
argc--;
diff --git a/toolbox/top.c b/toolbox/top.c
index b1a275c..1e99d4c 100644
--- a/toolbox/top.c
+++ b/toolbox/top.c
@@ -109,15 +109,9 @@
static int numcmp(long long a, long long b);
static void usage(char *cmd);
-static void exit_top(int signal) {
- exit(EXIT_FAILURE);
-}
-
int top_main(int argc, char *argv[]) {
num_used_procs = num_free_procs = 0;
- signal(SIGPIPE, exit_top);
-
max_procs = 0;
delay = 3;
iterations = -1;
diff --git a/toolbox/umount.c b/toolbox/umount.c
deleted file mode 100644
index 3e17396..0000000
--- a/toolbox/umount.c
+++ /dev/null
@@ -1,90 +0,0 @@
-
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <linux/loop.h>
-#include <errno.h>
-
-#define LOOPDEV_MAXLEN 64
-#define LOOP_MAJOR 7
-
-static int is_loop(char *dev)
-{
- struct stat st;
- int ret = 0;
-
- if (stat(dev, &st) == 0) {
- if (S_ISBLK(st.st_mode) && (major(st.st_rdev) == LOOP_MAJOR)) {
- ret = 1;
- }
- }
-
- return ret;
-}
-
-static int is_loop_mount(const char* path, char *loopdev)
-{
- FILE* f;
- int count;
- char device[256];
- char mount_path[256];
- char rest[256];
- int result = 0;
-
- f = fopen("/proc/mounts", "r");
- if (!f) {
- fprintf(stdout, "could not open /proc/mounts: %s\n", strerror(errno));
- return -1;
- }
-
- do {
- count = fscanf(f, "%255s %255s %255s\n", device, mount_path, rest);
- if (count == 3) {
- if (is_loop(device) && strcmp(path, mount_path) == 0) {
- strlcpy(loopdev, device, LOOPDEV_MAXLEN);
- result = 1;
- break;
- }
- }
- } while (count == 3);
-
- fclose(f);
- return result;
-}
-
-int umount_main(int argc, char *argv[])
-{
- int loop, loop_fd;
- char loopdev[LOOPDEV_MAXLEN];
-
- if(argc != 2) {
- fprintf(stderr,"umount <path>\n");
- return 1;
- }
-
- loop = is_loop_mount(argv[1], loopdev);
- if (umount(argv[1])) {
- fprintf(stderr, "failed: %s\n", strerror(errno));
- return 1;
- }
-
- if (loop) {
- // free the loop device
- loop_fd = open(loopdev, O_RDONLY);
- if (loop_fd < 0) {
- fprintf(stderr, "open loop device failed: %s\n", strerror(errno));
- return 1;
- }
- if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) {
- fprintf(stderr, "ioctl LOOP_CLR_FD failed: %s\n", strerror(errno));
- return 1;
- }
-
- close(loop_fd);
- }
-
- return 0;
-}
diff --git a/toolbox/uptime.c b/toolbox/uptime.c
index 2dd8084..ebfb15e 100644
--- a/toolbox/uptime.c
+++ b/toolbox/uptime.c
@@ -29,71 +29,33 @@
* SUCH DAMAGE.
*/
-#include <sys/time.h>
-#include <linux/ioctl.h>
-#include <linux/rtc.h>
-#include <linux/android_alarm.h>
-#include <fcntl.h>
+#include <errno.h>
#include <stdio.h>
+#include <string.h>
#include <time.h>
-#include <unistd.h>
static void format_time(int time, char* buffer) {
- int seconds, minutes, hours, days;
-
- seconds = time % 60;
+ int seconds = time % 60;
time /= 60;
- minutes = time % 60;
+ int minutes = time % 60;
time /= 60;
- hours = time % 24;
- days = time / 24;
+ int hours = time % 24;
+ int days = time / 24;
- if (days > 0)
- sprintf(buffer, "%d days, %02d:%02d:%02d", days, hours, minutes, seconds);
- else
+ if (days > 0) {
+ sprintf(buffer, "%d day%s, %02d:%02d:%02d", days, (days == 1) ? "" : "s", hours, minutes, seconds);
+ } else {
sprintf(buffer, "%02d:%02d:%02d", hours, minutes, seconds);
+ }
}
-static int elapsedRealtimeAlarm(struct timespec *ts)
-{
- int fd, result;
-
- fd = open("/dev/alarm", O_RDONLY);
- if (fd < 0)
- return fd;
-
- result = ioctl(fd, ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), ts);
- close(fd);
-
- return result;
-}
-
-int64_t elapsedRealtime()
-{
- struct timespec ts;
-
- int result = elapsedRealtimeAlarm(&ts);
- if (result < 0)
- result = clock_gettime(CLOCK_BOOTTIME, &ts);
-
- if (result == 0)
- return ts.tv_sec;
- return -1;
-}
-
-int uptime_main(int argc __attribute__((unused)),
- char *argv[] __attribute__((unused)))
-{
- float up_time, idle_time;
- char up_string[100], idle_string[100], sleep_string[100];
- int elapsed;
- struct timespec up_timespec;
-
+int uptime_main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) {
FILE* file = fopen("/proc/uptime", "r");
if (!file) {
fprintf(stderr, "Could not open /proc/uptime\n");
return -1;
}
+ float idle_time;
if (fscanf(file, "%*f %f", &idle_time) != 1) {
fprintf(stderr, "Could not parse /proc/uptime\n");
fclose(file);
@@ -101,18 +63,21 @@
}
fclose(file);
- if (clock_gettime(CLOCK_MONOTONIC, &up_timespec) < 0) {
- fprintf(stderr, "Could not get monotonic time\n");
+ struct timespec up_timespec;
+ if (clock_gettime(CLOCK_MONOTONIC, &up_timespec) == -1) {
+ fprintf(stderr, "Could not get monotonic time: %s\n", strerror(errno));
return -1;
}
- up_time = up_timespec.tv_sec + up_timespec.tv_nsec / 1e9;
+ float up_time = up_timespec.tv_sec + up_timespec.tv_nsec / 1e9;
- elapsed = elapsedRealtime();
- if (elapsed < 0) {
- fprintf(stderr, "elapsedRealtime failed\n");
+ struct timespec elapsed_timespec;
+ if (clock_gettime(CLOCK_BOOTTIME, &elapsed_timespec) == -1) {
+ fprintf(stderr, "Could not get boot time: %s\n", strerror(errno));
return -1;
}
+ int elapsed = elapsed_timespec.tv_sec;
+ char up_string[100], idle_string[100], sleep_string[100];
format_time(elapsed, up_string);
format_time((int)idle_time, idle_string);
format_time((int)(elapsed - up_time), sleep_string);
diff --git a/tzdatacheck/Android.mk b/tzdatacheck/Android.mk
new file mode 100644
index 0000000..0e25f7d
--- /dev/null
+++ b/tzdatacheck/Android.mk
@@ -0,0 +1,21 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# ========================================================
+# Executable
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= tzdatacheck.cpp
+LOCAL_MODULE := tzdatacheck
+LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
+LOCAL_CFLAGS := -Werror
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= tzdatacheck.cpp
+LOCAL_MODULE := tzdatacheck
+LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
+LOCAL_CFLAGS := -Werror
+include $(BUILD_HOST_EXECUTABLE)
+
diff --git a/tzdatacheck/tzdatacheck.cpp b/tzdatacheck/tzdatacheck.cpp
new file mode 100644
index 0000000..31f7b55
--- /dev/null
+++ b/tzdatacheck/tzdatacheck.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <ftw.h>
+#include <libgen.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+
+static const char* TZDATA_FILENAME = "/tzdata";
+// tzdata file header (as much as we need for the version):
+// byte[11] tzdata_version -- e.g. "tzdata2012f"
+static const int TZ_HEADER_LENGTH = 11;
+
+static void usage() {
+ std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n"
+ "\n"
+ "Compares the headers of two tzdata files. If the one in SYSTEM_TZ_DIR is the\n"
+ "same or a higher version than the one in DATA_TZ_DIR the DATA_TZ_DIR is renamed\n"
+ "and then deleted.\n";
+ exit(1);
+}
+
+/*
+ * Opens a file and fills headerBytes with the first byteCount bytes from the file. It is a fatal
+ * error if the file is too small or cannot be opened. If the file does not exist false is returned.
+ * If the bytes were read successfully then true is returned.
+ */
+static bool readHeader(const std::string& tzDataFileName, char* headerBytes, size_t byteCount) {
+ FILE* tzDataFile = fopen(tzDataFileName.c_str(), "r");
+ if (tzDataFile == nullptr) {
+ if (errno == ENOENT) {
+ return false;
+ } else {
+ PLOG(FATAL) << "Error opening tzdata file " << tzDataFileName;
+ }
+ }
+ size_t bytesRead = fread(headerBytes, 1, byteCount, tzDataFile);
+ if (bytesRead != byteCount) {
+ LOG(FATAL) << tzDataFileName << " is too small. " << byteCount << " bytes required";
+ }
+ fclose(tzDataFile);
+ return true;
+}
+
+/* Checks the contents of headerBytes. It is a fatal error if it not a tzdata header. */
+static void checkValidHeader(const std::string& fileName, char* headerBytes) {
+ if (strncmp("tzdata", headerBytes, 6) != 0) {
+ LOG(FATAL) << fileName << " does not start with the expected bytes (tzdata)";
+ }
+}
+
+/* Return the parent directory of dirName. */
+static std::string getParentDir(const std::string& dirName) {
+ std::unique_ptr<char> mutable_dirname(strdup(dirName.c_str()));
+ return dirname(mutable_dirname.get());
+}
+
+/* Deletes a single file, symlink or directory. Called from nftw(). */
+static int deleteFn(const char* fpath, const struct stat*, int typeflag, struct FTW*) {
+ LOG(DEBUG) << "Inspecting " << fpath;
+ switch (typeflag) {
+ case FTW_F:
+ case FTW_SL:
+ LOG(DEBUG) << "Unlinking " << fpath;
+ if (unlink(fpath)) {
+ PLOG(WARNING) << "Failed to unlink file/symlink " << fpath;
+ }
+ break;
+ case FTW_D:
+ case FTW_DP:
+ LOG(DEBUG) << "Removing dir " << fpath;
+ if (rmdir(fpath)) {
+ PLOG(WARNING) << "Failed to remove dir " << fpath;
+ }
+ break;
+ default:
+ LOG(WARNING) << "Unsupported file type " << fpath << ": " << typeflag;
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Deletes dirToDelete and returns true if it is successful in removing or moving the directory out
+ * of the way. If dirToDelete does not exist this function does nothing and returns true.
+ *
+ * During deletion, this function first renames the directory to a temporary name. If the temporary
+ * directory cannot be created, or the directory cannot be renamed, false is returned. After the
+ * rename, deletion of files and subdirs beneath the directory is performed on a "best effort"
+ * basis. Symlinks beneath the directory are not followed.
+ */
+static bool deleteDir(const std::string& dirToDelete) {
+ // Check whether the dir exists.
+ struct stat buf;
+ if (stat(dirToDelete.c_str(), &buf) == 0) {
+ if (!S_ISDIR(buf.st_mode)) {
+ LOG(WARNING) << dirToDelete << " is not a directory";
+ return false;
+ }
+ } else {
+ if (errno == ENOENT) {
+ PLOG(INFO) << "Directory does not exist: " << dirToDelete;
+ return true;
+ } else {
+ PLOG(WARNING) << "Unable to stat " << dirToDelete;
+ return false;
+ }
+ }
+
+ // First, rename dirToDelete.
+ std::string tempDirNameTemplate = getParentDir(dirToDelete);
+ tempDirNameTemplate += "/tempXXXXXX";
+
+ // Create an empty directory with the temporary name. For this we need a non-const char*.
+ std::vector<char> tempDirName(tempDirNameTemplate.length() + 1);
+ strcpy(&tempDirName[0], tempDirNameTemplate.c_str());
+ if (mkdtemp(&tempDirName[0]) == nullptr) {
+ PLOG(WARNING) << "Unable to create a temporary directory: " << tempDirNameTemplate;
+ return false;
+ }
+
+ // Rename dirToDelete to tempDirName.
+ int rc = rename(dirToDelete.c_str(), &tempDirName[0]);
+ if (rc == -1) {
+ PLOG(WARNING) << "Unable to rename directory from " << dirToDelete << " to "
+ << &tempDirName[0];
+ return false;
+ }
+
+ // Recursively delete contents of tempDirName.
+ rc = nftw(&tempDirName[0], deleteFn, 10 /* openFiles */,
+ FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
+ if (rc == -1) {
+ LOG(INFO) << "Could not delete directory: " << &tempDirName[0];
+ }
+ return true;
+}
+
+/*
+ * After a platform update it is likely that timezone data found on the system partition will be
+ * newer than the version found in the data partition. This tool detects this case and removes the
+ * version in /data along with any update metadata.
+ *
+ * Note: This code is related to code in com.android.server.updates.TzDataInstallReceiver. The
+ * paths for the metadata and current timezone data must match.
+ *
+ * Typically on device the two args will be:
+ * /system/usr/share/zoneinfo /data/misc/zoneinfo
+ *
+ * See usage() for usage notes.
+ */
+int main(int argc, char* argv[]) {
+ if (argc != 3) {
+ usage();
+ }
+
+ const char* systemZoneInfoDir = argv[1];
+ const char* dataZoneInfoDir = argv[2];
+
+ std::string dataCurrentDirName(dataZoneInfoDir);
+ dataCurrentDirName += "/current";
+ std::string dataTzDataFileName(dataCurrentDirName);
+ dataTzDataFileName += TZDATA_FILENAME;
+
+ std::vector<char> dataTzDataHeader;
+ dataTzDataHeader.reserve(TZ_HEADER_LENGTH);
+
+ bool dataFileExists = readHeader(dataTzDataFileName, dataTzDataHeader.data(), TZ_HEADER_LENGTH);
+ if (!dataFileExists) {
+ LOG(INFO) << "tzdata file " << dataTzDataFileName << " does not exist. No action required.";
+ return 0;
+ }
+ checkValidHeader(dataTzDataFileName, dataTzDataHeader.data());
+
+ std::string systemTzDataFileName(systemZoneInfoDir);
+ systemTzDataFileName += TZDATA_FILENAME;
+ std::vector<char> systemTzDataHeader;
+ systemTzDataHeader.reserve(TZ_HEADER_LENGTH);
+ bool systemFileExists =
+ readHeader(systemTzDataFileName, systemTzDataHeader.data(), TZ_HEADER_LENGTH);
+ if (!systemFileExists) {
+ LOG(FATAL) << systemTzDataFileName << " does not exist or could not be opened";
+ }
+ checkValidHeader(systemTzDataFileName, systemTzDataHeader.data());
+
+ if (strncmp(&systemTzDataHeader[0], &dataTzDataHeader[0], TZ_HEADER_LENGTH) < 0) {
+ LOG(INFO) << "tzdata file " << dataTzDataFileName << " is the newer than "
+ << systemTzDataFileName << ". No action required.";
+ } else {
+ // We have detected the case this tool is intended to prevent. Go fix it.
+ LOG(INFO) << "tzdata file " << dataTzDataFileName << " is the same as or older than "
+ << systemTzDataFileName << "; fixing...";
+
+ // Delete the update metadata
+ std::string dataUpdatesDirName(dataZoneInfoDir);
+ dataUpdatesDirName += "/updates";
+ LOG(INFO) << "Removing: " << dataUpdatesDirName;
+ bool deleted = deleteDir(dataUpdatesDirName);
+ if (!deleted) {
+ LOG(WARNING) << "Deletion of install metadata " << dataUpdatesDirName
+ << " was not successful";
+ }
+
+ // Delete the TZ data
+ LOG(INFO) << "Removing: " << dataCurrentDirName;
+ deleted = deleteDir(dataCurrentDirName);
+ if (!deleted) {
+ LOG(WARNING) << "Deletion of tzdata " << dataCurrentDirName << " was not successful";
+ }
+ }
+
+ return 0;
+}