Merge "Enable seccomp in init with generated policy"
diff --git a/Android.bp b/Android.bp
index b44c296..c6f6251 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1 +1,6 @@
+filegroup {
+ name: "android_filesystem_config_header",
+ srcs: ["include/private/android_filesystem_config.h"],
+}
+
subdirs = ["*"]
diff --git a/adb/Android.mk b/adb/Android.mk
index fab8c87..2f5a2ee 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -336,13 +336,13 @@
libfec_rs \
libselinux \
liblog \
- libext4_utils_static \
+ libext4_utils \
libsquashfs_utils \
libcutils \
libbase \
libcrypto_utils \
libcrypto \
libminijail \
- libdebuggerd_client \
+ libdebuggerd_handler \
include $(BUILD_EXECUTABLE)
diff --git a/adb/adb.h b/adb/adb.h
index 19f09a3..7db3b15 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -218,8 +218,6 @@
#define CHUNK_SIZE (64*1024)
#if !ADB_HOST
-#define USB_ADB_PATH "/dev/android_adb"
-
#define USB_FFS_ADB_PATH "/dev/usb-ffs/adb/"
#define USB_FFS_ADB_EP(x) USB_FFS_ADB_PATH#x
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index a064de2..2befa0c 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -91,9 +91,9 @@
"global options:\n"
" -a listen on all network interfaces, not just localhost\n"
" -d use USB device (error if multiple devices connected)\n"
- " -e use emulator (error if multiple emulators running)\n"
+ " -e use TCP/IP device (error if multiple TCP/IP devices available)\n"
" -s SERIAL\n"
- " use device/emulator with given serial number (overrides $ANDROID_SERIAL)\n"
+ " use device with given serial number (overrides $ANDROID_SERIAL)\n"
" -p PRODUCT\n"
" name or path ('angler'/'out/target/product/angler');\n"
" default $ANDROID_PRODUCT_OUT\n"
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 78434a0..6382b67 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -34,9 +34,9 @@
#include <libminijail.h>
#include <scoped_minijail.h>
-#include "debuggerd/client.h"
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
+#include "debuggerd/handler.h"
#include "selinux/android.h"
#include "adb.h"
@@ -170,7 +170,7 @@
drop_privileges(server_port);
bool is_usb = false;
- if (access(USB_ADB_PATH, F_OK) == 0 || access(USB_FFS_ADB_EP0, F_OK) == 0) {
+ if (access(USB_FFS_ADB_EP0, F_OK) == 0) {
// Listen on USB.
usb_init();
is_usb = true;
diff --git a/adb/usb_linux_client.cpp b/adb/usb_linux_client.cpp
index 1cc7f68..a21a193 100644
--- a/adb/usb_linux_client.cpp
+++ b/adb/usb_linux_client.cpp
@@ -74,9 +74,6 @@
void (*kick)(usb_handle *h);
void (*close)(usb_handle *h);
- // Legacy f_adb
- int fd = -1;
-
// FunctionFS
int control = -1;
int bulk_out = -1; /* "out" from the host's perspective => source for adbd */
@@ -248,141 +245,7 @@
},
};
-static void usb_adb_open_thread(void* x) {
- struct usb_handle *usb = (struct usb_handle *)x;
- int fd;
-
- adb_thread_setname("usb open");
-
- while (true) {
- // wait until the USB device needs opening
- std::unique_lock<std::mutex> lock(usb->lock);
- while (!usb->open_new_connection) {
- usb->notify.wait(lock);
- }
- usb->open_new_connection = false;
- lock.unlock();
-
- D("[ usb_thread - opening device ]");
- do {
- /* XXX use inotify? */
- fd = unix_open("/dev/android_adb", O_RDWR);
- if (fd < 0) {
- // to support older kernels
- fd = unix_open("/dev/android", O_RDWR);
- }
- if (fd < 0) {
- std::this_thread::sleep_for(1s);
- }
- } while (fd < 0);
- D("[ opening device succeeded ]");
-
- close_on_exec(fd);
- usb->fd = fd;
-
- D("[ usb_thread - registering device ]");
- register_usb_transport(usb, 0, 0, 1);
- }
-
- // never gets here
- abort();
-}
-
-static int usb_adb_write(usb_handle *h, const void *data, int len)
-{
- int n;
-
- D("about to write (fd=%d, len=%d)", h->fd, len);
- n = unix_write(h->fd, data, len);
- if(n != len) {
- D("ERROR: fd = %d, n = %d, errno = %d (%s)",
- h->fd, n, errno, strerror(errno));
- return -1;
- }
- if (h->kicked) {
- D("usb_adb_write finished due to kicked");
- return -1;
- }
- D("[ done fd=%d ]", h->fd);
- return 0;
-}
-
-static int usb_adb_read(usb_handle *h, void *data, int len)
-{
- D("about to read (fd=%d, len=%d)", h->fd, len);
- while (len > 0) {
- // The kernel implementation of adb_read in f_adb.c doesn't support
- // reads larger then 4096 bytes. Read the data in 4096 byte chunks to
- // avoid the issue. (The ffs implementation doesn't have this limit.)
- int bytes_to_read = len < 4096 ? len : 4096;
- int n = unix_read(h->fd, data, bytes_to_read);
- if (n != bytes_to_read) {
- D("ERROR: fd = %d, n = %d, errno = %d (%s)",
- h->fd, n, errno, strerror(errno));
- return -1;
- }
- if (h->kicked) {
- D("usb_adb_read finished due to kicked");
- return -1;
- }
- len -= n;
- data = ((char*)data) + n;
- }
- D("[ done fd=%d ]", h->fd);
- return 0;
-}
-
-static void usb_adb_kick(usb_handle *h) {
- D("usb_kick");
- // Other threads may be calling usb_adb_read/usb_adb_write at the same time.
- // If we close h->fd, the file descriptor will be reused to open other files,
- // and the read/write thread may operate on the wrong file. So instead
- // we set the kicked flag and reopen h->fd to a dummy file here. After read/write
- // threads finish, we close h->fd in usb_adb_close().
- h->kicked = true;
- TEMP_FAILURE_RETRY(dup2(dummy_fd, h->fd));
-}
-
-static void usb_adb_close(usb_handle *h) {
- h->kicked = false;
- adb_close(h->fd);
- // Notify usb_adb_open_thread to open a new connection.
- h->lock.lock();
- h->open_new_connection = true;
- h->lock.unlock();
- h->notify.notify_one();
-}
-
-static void usb_adb_init()
-{
- usb_handle* h = new usb_handle();
-
- h->write = usb_adb_write;
- h->read = usb_adb_read;
- h->kick = usb_adb_kick;
- h->close = usb_adb_close;
-
- // 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.
- int fd = unix_open("/dev/android_adb_enable", O_RDWR);
- if (fd < 0) {
- D("failed to open /dev/android_adb_enable");
- } else {
- close_on_exec(fd);
- }
-
- D("[ usb_init - starting thread ]");
- if (!adb_thread_create(usb_adb_open_thread, h)) {
- fatal_errno("cannot create usb thread");
- }
-}
-
-
-static bool init_functionfs(struct usb_handle *h)
-{
+static bool init_functionfs(struct usb_handle* h) {
ssize_t ret;
struct desc_v1 v1_descriptor;
struct desc_v2 v2_descriptor;
@@ -463,7 +326,7 @@
}
static void usb_ffs_open_thread(void* x) {
- struct usb_handle *usb = (struct usb_handle *)x;
+ struct usb_handle* usb = (struct usb_handle*)x;
adb_thread_setname("usb ffs open");
@@ -530,8 +393,7 @@
return 0;
}
-static void usb_ffs_kick(usb_handle *h)
-{
+static void usb_ffs_kick(usb_handle* h) {
int err;
err = ioctl(h->bulk_in, FUNCTIONFS_CLEAR_HALT);
@@ -553,7 +415,7 @@
TEMP_FAILURE_RETRY(dup2(dummy_fd, h->bulk_in));
}
-static void usb_ffs_close(usb_handle *h) {
+static void usb_ffs_close(usb_handle* h) {
h->kicked = false;
adb_close(h->bulk_out);
adb_close(h->bulk_in);
@@ -564,8 +426,7 @@
h->notify.notify_one();
}
-static void usb_ffs_init()
-{
+static void usb_ffs_init() {
D("[ usb_init - using FunctionFS ]");
usb_handle* h = new usb_handle();
@@ -581,33 +442,25 @@
}
}
-void usb_init()
-{
+void usb_init() {
dummy_fd = adb_open("/dev/null", O_WRONLY);
CHECK_NE(dummy_fd, -1);
- if (access(USB_FFS_ADB_EP0, F_OK) == 0)
- usb_ffs_init();
- else
- usb_adb_init();
+ usb_ffs_init();
}
-int usb_write(usb_handle *h, const void *data, int len)
-{
+int usb_write(usb_handle* h, const void* data, int len) {
return h->write(h, data, len);
}
-int usb_read(usb_handle *h, void *data, int len)
-{
+int usb_read(usb_handle* h, void* data, int len) {
return h->read(h, data, len);
}
-int usb_close(usb_handle *h)
-{
+int usb_close(usb_handle* h) {
h->close(h);
return 0;
}
-void usb_kick(usb_handle *h)
-{
+void usb_kick(usb_handle* h) {
h->kick(h);
}
diff --git a/adf/libadfhwc/adfhwc.cpp b/adf/libadfhwc/adfhwc.cpp
index 7d5b555..a97862a 100644
--- a/adf/libadfhwc/adfhwc.cpp
+++ b/adf/libadfhwc/adfhwc.cpp
@@ -20,7 +20,7 @@
#include <pthread.h>
#include <sys/resource.h>
-#include <android/log.h>
+#include <log/log.h>
#include <utils/Vector.h>
#include <adf/adf.h>
diff --git a/base/file_test.cpp b/base/file_test.cpp
index f741d89..ed39ce9 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -134,7 +134,11 @@
// Linux doesn't allow empty symbolic links.
std::string min("x");
// ext2 and ext4 both have PAGE_SIZE limits.
- std::string max(static_cast<size_t>(4096 - 1), 'x');
+ // If file encryption is enabled, there's extra overhead to store the
+ // size of the encrypted symlink target. There's also an off-by-one
+ // in current kernels (and marlin/sailfish where we're seeing this
+ // failure are still on 3.18, far from current). http://b/33306057.
+ std::string max(static_cast<size_t>(4096 - 2 - 1 - 1), 'x');
TemporaryDir td;
std::string min_path{std::string(td.path) + "/" + "min"};
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index b8a9289..f5f5c11 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -64,6 +64,9 @@
bool EndsWith(const std::string& s, const char* suffix);
bool EndsWithIgnoreCase(const std::string& s, const char* suffix);
+// Tests whether 'lhs' equals 'rhs', ignoring case.
+bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs);
+
} // namespace base
} // namespace android
diff --git a/base/strings.cpp b/base/strings.cpp
index 7a94ad7..46fe939 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -112,5 +112,9 @@
return EndsWith(s, suffix, false);
}
+bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs) {
+ return strcasecmp(lhs.c_str(), rhs.c_str()) == 0;
+}
+
} // namespace base
} // namespace android
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index 5fb21dd..7a65a00 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -244,3 +244,10 @@
ASSERT_FALSE(android::base::EndsWithIgnoreCase("foobar", "OBA"));
ASSERT_FALSE(android::base::EndsWithIgnoreCase("foobar", "FOO"));
}
+
+TEST(strings, EqualsIgnoreCase) {
+ ASSERT_TRUE(android::base::EqualsIgnoreCase("foo", "FOO"));
+ ASSERT_TRUE(android::base::EqualsIgnoreCase("FOO", "foo"));
+ ASSERT_FALSE(android::base::EqualsIgnoreCase("foo", "bar"));
+ ASSERT_FALSE(android::base::EqualsIgnoreCase("foo", "fool"));
+}
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index c96e996..ca8bea2 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -11,8 +11,26 @@
on post-fs-data && property:init.svc.bootanim=running
exec - root root -- /system/bin/bootstat -r post_decrypt_time_elapsed
+# sys.logbootcomplete is a signal to enable the bootstat logging mechanism.
+# This signaling is necessary to prevent logging boot metrics after a soft
+# reboot (e.g., adb shell stop && adb shell start). /proc/uptime is not reset
+# during a soft reboot, which leads to false boot time metrics being reported.
+#
+# The 'on boot' event occurs once per hard boot (device power on), which
+# switches the flag on. If the device performs a soft reboot, the flag is
+# switched off and cannot be switched on until the device hard boots again.
+
+# Enable bootstat logging on boot.
+on boot
+ setprop sys.logbootcomplete 1
+
+# Disable further bootstat logging on a soft reboot. A soft reboot is
+# signaled by the zygote stopping.
+on property:init.svc.zygote=stopping
+ setprop sys.logbootcomplete 0
+
# Record boot complete metrics.
-on property:sys.boot_completed=1
+on property:sys.boot_completed=1 && property:sys.logbootcomplete=1
# Record boot_complete and related stats (decryption, etc).
exec - root root -- /system/bin/bootstat --record_boot_complete
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index f0131b8..6dc6675 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -1,25 +1,197 @@
+cc_defaults {
+ name: "debuggerd_defaults",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Wno-error",
+ "-Wno-nullability-completeness",
+ "-Os",
+ ],
+
+ local_include_dirs: ["include"],
+}
+
cc_library_static {
- name: "libdebuggerd_client",
- srcs: ["client/debuggerd_client.cpp"],
+ name: "libdebuggerd_handler",
+ defaults: ["debuggerd_defaults"],
+ srcs: ["handler/debuggerd_handler.cpp"],
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- "-Os",
- ],
+ // libdebuggerd_handler gets async signal safe logging via libc_logging,
+ // which defines its interface in bionic private headers.
+ include_dirs: ["bionic/libc"],
+ static_libs: ["libc_logging"],
- target: {
- android64: {
- cflags: ["-DTARGET_IS_64_BIT"],
+ export_include_dirs: ["include"],
+}
+
+cc_library {
+ name: "libdebuggerd_client",
+ defaults: ["debuggerd_defaults"],
+ srcs: [
+ "client/debuggerd_client.cpp",
+ "util.cpp",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ ],
+ export_include_dirs: ["include"],
+}
+
+cc_library {
+ name: "libdebuggerd",
+ defaults: ["debuggerd_defaults"],
+
+ srcs: [
+ "libdebuggerd/backtrace.cpp",
+ "libdebuggerd/elf_utils.cpp",
+ "libdebuggerd/open_files_list.cpp",
+ "libdebuggerd/tombstone.cpp",
+ "libdebuggerd/utility.cpp",
+ ],
+
+ target: {
+ android_arm: {
+ srcs: ["libdebuggerd/arm/machine.cpp"],
+ },
+ android_arm64: {
+ srcs: ["libdebuggerd/arm64/machine.cpp"],
+ },
+ android_mips: {
+ srcs: ["libdebuggerd/mips/machine.cpp"],
+ },
+ android_mips64: {
+ srcs: ["libdebuggerd/mips64/machine.cpp"],
+ },
+ android_x86: {
+ srcs: ["libdebuggerd/x86/machine.cpp"],
+ },
+ android_x86_64: {
+ srcs: ["libdebuggerd/x86_64/machine.cpp"],
+ },
},
- },
- local_include_dirs: ["include"],
- export_include_dirs: ["include"],
+ local_include_dirs: ["libdebuggerd/include"],
+ export_include_dirs: ["libdebuggerd/include"],
- // libdebuggerd_client gets async signal safe logging via libc_logging,
- // which defines its interface in bionic private headers.
- include_dirs: ["bionic/libc"],
- static_libs: ["libc_logging"],
+ shared_libs: [
+ "libbacktrace",
+ "libbase",
+ "libcutils",
+ "liblog",
+ ],
+}
+
+cc_test {
+ name: "debuggerd_test",
+ defaults: ["debuggerd_defaults"],
+
+ cflags: ["-Wno-missing-field-initializers"],
+ srcs: [
+ "libdebuggerd/test/dump_memory_test.cpp",
+ "libdebuggerd/test/elf_fake.cpp",
+ "libdebuggerd/test/log_fake.cpp",
+ "libdebuggerd/test/open_files_list_test.cpp",
+ "libdebuggerd/test/property_fake.cpp",
+ "libdebuggerd/test/ptrace_fake.cpp",
+ "libdebuggerd/test/tombstone_test.cpp",
+ ],
+
+ target: {
+ android: {
+ srcs: [
+ "debuggerd_test.cpp",
+ "util.cpp"
+ ],
+ },
+ },
+
+ shared_libs: [
+ "libbacktrace",
+ "libbase",
+ "libcutils",
+ ],
+
+ static_libs: [
+ "libdebuggerd"
+ ],
+
+ local_include_dirs: [
+ "libdebuggerd",
+ ],
+
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ stem: "debuggerd_test32",
+ },
+ lib64: {
+ stem: "debuggerd_test64",
+ },
+ },
+}
+
+cc_binary {
+ name: "crash_dump",
+ srcs: [
+ "crash_dump.cpp",
+ "util.cpp",
+ ],
+ defaults: ["debuggerd_defaults"],
+
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+
+ shared_libs: [
+ "libbacktrace",
+ "libbase",
+ "libdebuggerd",
+ "liblog",
+ "libprocinfo",
+ "libselinux",
+ ],
+}
+
+cc_binary {
+ name: "debuggerd",
+ srcs: [
+ "debuggerd.cpp",
+ ],
+ defaults: ["debuggerd_defaults"],
+
+ shared_libs: [
+ "libbase",
+ "libdebuggerd_client",
+ "liblog",
+ "libselinux",
+ ],
+
+ local_include_dirs: ["include"],
+}
+
+cc_binary {
+ name: "tombstoned",
+ srcs: [
+ "util.cpp",
+ "tombstoned/intercept_manager.cpp",
+ "tombstoned/tombstoned.cpp",
+ ],
+ defaults: ["debuggerd_defaults"],
+
+ static_libs: [
+ "libbase",
+ "libcutils",
+ "libevent",
+ "liblog",
+ ],
+
+ init_rc: ["tombstoned/tombstoned.rc"]
}
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
deleted file mode 100644
index e3bdd43..0000000
--- a/debuggerd/Android.mk
+++ /dev/null
@@ -1,167 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-common_cppflags := \
- -W \
- -Wall \
- -Wextra \
- -Wunused \
- -Werror \
-
-ifeq ($(TARGET_IS_64_BIT),true)
-common_cppflags += -DTARGET_IS_64_BIT
-endif
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- backtrace.cpp \
- debuggerd.cpp \
- elf_utils.cpp \
- getevent.cpp \
- open_files_list.cpp \
- signal_sender.cpp \
- tombstone.cpp \
- utility.cpp \
-
-LOCAL_SRC_FILES_arm := arm/machine.cpp
-LOCAL_SRC_FILES_arm64 := arm64/machine.cpp
-LOCAL_SRC_FILES_mips := mips/machine.cpp
-LOCAL_SRC_FILES_mips64 := mips64/machine.cpp
-LOCAL_SRC_FILES_x86 := x86/machine.cpp
-LOCAL_SRC_FILES_x86_64 := x86_64/machine.cpp
-
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CPPFLAGS := $(common_cppflags)
-
-LOCAL_INIT_RC_32 := debuggerd.rc
-LOCAL_INIT_RC_64 := debuggerd64.rc
-
-LOCAL_SHARED_LIBRARIES := \
- libbacktrace \
- libbase \
- libcutils \
- liblog \
- libselinux \
-
-LOCAL_CLANG := true
-
-LOCAL_MODULE := debuggerd
-LOCAL_MODULE_STEM_32 := debuggerd
-LOCAL_MODULE_STEM_64 := debuggerd64
-LOCAL_MULTILIB := both
-
-include $(BUILD_EXECUTABLE)
-
-crasher_cppflags := $(common_cppflags) -O0 -fstack-protector-all -Wno-free-nonheap-object
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := crasher.cpp
-LOCAL_SRC_FILES_arm := arm/crashglue.S
-LOCAL_SRC_FILES_arm64 := arm64/crashglue.S
-LOCAL_SRC_FILES_mips := mips/crashglue.S
-LOCAL_SRC_FILES_mips64 := mips64/crashglue.S
-LOCAL_SRC_FILES_x86 := x86/crashglue.S
-LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S
-LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-LOCAL_MODULE_TAGS := optional
-LOCAL_CPPFLAGS := $(crasher_cppflags)
-LOCAL_SHARED_LIBRARIES := libbase liblog
-
-# The arm emulator has VFP but not VFPv3-D32.
-ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
-LOCAL_ASFLAGS_arm += -DHAS_VFP_D32
-endif
-
-LOCAL_MODULE := crasher
-LOCAL_MODULE_STEM_32 := crasher
-LOCAL_MODULE_STEM_64 := crasher64
-LOCAL_MULTILIB := both
-
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := crasher.cpp
-LOCAL_SRC_FILES_arm := arm/crashglue.S
-LOCAL_SRC_FILES_arm64 := arm64/crashglue.S
-LOCAL_SRC_FILES_mips := mips/crashglue.S
-LOCAL_SRC_FILES_mips64 := mips64/crashglue.S
-LOCAL_SRC_FILES_x86 := x86/crashglue.S
-LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S
-LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-LOCAL_MODULE_TAGS := optional
-LOCAL_CPPFLAGS := $(crasher_cppflags) -DSTATIC_CRASHER
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-
-# The arm emulator has VFP but not VFPv3-D32.
-ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
-LOCAL_ASFLAGS_arm += -DHAS_VFP_D32
-endif
-
-LOCAL_MODULE := static_crasher
-LOCAL_MODULE_STEM_32 := static_crasher
-LOCAL_MODULE_STEM_64 := static_crasher64
-LOCAL_MULTILIB := both
-
-LOCAL_STATIC_LIBRARIES := libdebuggerd_client libbase liblog
-
-include $(BUILD_EXECUTABLE)
-
-debuggerd_test_src_files := \
- utility.cpp \
- open_files_list.cpp \
- test/dump_memory_test.cpp \
- test/elf_fake.cpp \
- test/log_fake.cpp \
- test/open_files_list_test.cpp \
- test/property_fake.cpp \
- test/ptrace_fake.cpp \
- test/tombstone_test.cpp \
- test/selinux_fake.cpp \
-
-debuggerd_shared_libraries := \
- libbacktrace \
- libbase \
- libcutils \
- liblog
-
-debuggerd_c_includes := \
- $(LOCAL_PATH)/test \
-
-debuggerd_cpp_flags := \
- $(common_cppflags) \
- -Wno-missing-field-initializers \
- -fno-rtti \
-
-# Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374
-debuggerd_cpp_flags += -Wno-varargs
-
-# Only build the host tests on linux.
-ifeq ($(HOST_OS),linux)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := debuggerd_test
-LOCAL_SRC_FILES := $(debuggerd_test_src_files)
-LOCAL_SHARED_LIBRARIES := $(debuggerd_shared_libraries)
-LOCAL_C_INCLUDES := $(debuggerd_c_includes)
-LOCAL_CPPFLAGS := $(debuggerd_cpp_flags)
-
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_MULTILIB := both
-include $(BUILD_HOST_NATIVE_TEST)
-
-endif
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := debuggerd_test
-LOCAL_SRC_FILES := $(debuggerd_test_src_files)
-LOCAL_SHARED_LIBRARIES := $(debuggerd_shared_libraries)
-LOCAL_C_INCLUDES := $(debuggerd_c_includes)
-LOCAL_CPPFLAGS := $(debuggerd_cpp_flags)
-
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_MULTILIB := both
-include $(BUILD_NATIVE_TEST)
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index c67d747..81d70df 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -1,379 +1,189 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
+ * Copyright 2016, The Android Open Source Project
*
- * 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.
+ * 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
*
- * 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.
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
-#include "debuggerd/client.h"
+#include <debuggerd/client.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <pthread.h>
-#include <sched.h>
+#include <fcntl.h>
#include <signal.h>
-#include <stddef.h>
-#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-#include <sys/socket.h>
-#include <sys/syscall.h>
-#include <sys/un.h>
-#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <unistd.h>
-#include "private/libc_logging.h"
+#include <chrono>
-#if defined(TARGET_IS_64_BIT) && !defined(__LP64__)
-#define SOCKET_NAME "android:debuggerd32"
-#else
-#define SOCKET_NAME "android:debuggerd"
-#endif
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+#include <debuggerd/handler.h>
+#include <debuggerd/protocol.h>
+#include <debuggerd/util.h>
-// see man(2) prctl, specifically the section about PR_GET_NAME
-#define MAX_TASK_NAME_LEN (16)
+using android::base::unique_fd;
-static debuggerd_callbacks_t g_callbacks;
-
-// Don't use __libc_fatal because it exits via abort, which might put us back into a signal handler.
-#define fatal(...) \
- do { \
- __libc_format_log(ANDROID_LOG_FATAL, "libc", __VA_ARGS__); \
- _exit(1); \
- } while (0)
-
-static int socket_abstract_client(const char* name, int type) {
- sockaddr_un addr;
-
- // Test with length +1 for the *initial* '\0'.
- size_t namelen = strlen(name);
- if ((namelen + 1) > sizeof(addr.sun_path)) {
- errno = EINVAL;
- return -1;
- }
-
- // This is used for abstract socket namespace, we need
- // an initial '\0' at the start of the Unix socket path.
- //
- // Note: The path in this case is *not* supposed to be
- // '\0'-terminated. ("man 7 unix" for the gory details.)
- memset(&addr, 0, sizeof(addr));
- addr.sun_family = AF_LOCAL;
- addr.sun_path[0] = 0;
- memcpy(addr.sun_path + 1, name, namelen);
-
- socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;
-
- int s = socket(AF_LOCAL, type, 0);
- if (s == -1) {
- return -1;
- }
-
- int rc = TEMP_FAILURE_RETRY(connect(s, reinterpret_cast<sockaddr*>(&addr), alen));
- if (rc == -1) {
- close(s);
- return -1;
- }
-
- return s;
-}
-
-/*
- * Writes a summary of the signal to the log file. We do this so that, if
- * for some reason we're not able to contact debuggerd, there is still some
- * indication of the failure in the log.
- *
- * We could be here as a result of native heap corruption, or while a
- * mutex is being held, so we don't want to use any libc functions that
- * could allocate memory or hold a lock.
- */
-static void log_signal_summary(int signum, const siginfo_t* info) {
- const char* signal_name = "???";
- bool has_address = false;
- switch (signum) {
- case SIGABRT:
- signal_name = "SIGABRT";
- break;
- case SIGBUS:
- signal_name = "SIGBUS";
- has_address = true;
- break;
- case SIGFPE:
- signal_name = "SIGFPE";
- has_address = true;
- break;
- case SIGILL:
- signal_name = "SIGILL";
- has_address = true;
- break;
- case SIGSEGV:
- signal_name = "SIGSEGV";
- has_address = true;
- break;
-#if defined(SIGSTKFLT)
- case SIGSTKFLT:
- signal_name = "SIGSTKFLT";
- break;
-#endif
- case SIGSYS:
- signal_name = "SIGSYS";
- break;
- case SIGTRAP:
- signal_name = "SIGTRAP";
- break;
- }
-
- char thread_name[MAX_TASK_NAME_LEN + 1]; // one more for termination
- if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
- strcpy(thread_name, "<name unknown>");
- } else {
- // short names are null terminated by prctl, but the man page
- // implies that 16 byte names are not.
- thread_name[MAX_TASK_NAME_LEN] = 0;
- }
-
- // "info" will be null if the siginfo_t information was not available.
- // Many signals don't have an address or a code.
- char code_desc[32]; // ", code -6"
- char addr_desc[32]; // ", fault addr 0x1234"
- addr_desc[0] = code_desc[0] = 0;
- if (info != nullptr) {
- // For a rethrown signal, this si_code will be right and the one debuggerd shows will
- // always be SI_TKILL.
- __libc_format_buffer(code_desc, sizeof(code_desc), ", code %d", info->si_code);
- if (has_address) {
- __libc_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
- }
- }
- __libc_format_log(ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s)%s%s in tid %d (%s)", signum,
- signal_name, code_desc, addr_desc, gettid(), thread_name);
-}
-
-/*
- * Returns true if the handler for signal "signum" has SA_SIGINFO set.
- */
-static bool have_siginfo(int signum) {
- struct sigaction old_action, new_action;
-
- memset(&new_action, 0, sizeof(new_action));
- new_action.sa_handler = SIG_DFL;
- new_action.sa_flags = SA_RESTART;
- sigemptyset(&new_action.sa_mask);
-
- if (sigaction(signum, &new_action, &old_action) < 0) {
- __libc_format_log(ANDROID_LOG_WARN, "libc", "Failed testing for SA_SIGINFO: %s",
- strerror(errno));
+static bool send_signal(pid_t pid, bool backtrace) {
+ sigval val;
+ val.sival_int = backtrace;
+ if (sigqueue(pid, DEBUGGER_SIGNAL, val) != 0) {
+ PLOG(ERROR) << "libdebuggerd_client: failed to send signal to pid " << pid;
return false;
}
- bool result = (old_action.sa_flags & SA_SIGINFO) != 0;
-
- if (sigaction(signum, &old_action, nullptr) == -1) {
- __libc_format_log(ANDROID_LOG_WARN, "libc", "Restore failed in test for SA_SIGINFO: %s",
- strerror(errno));
- }
- return result;
+ return true;
}
-static void send_debuggerd_packet(pid_t crashing_tid, pid_t pseudothread_tid) {
- // Mutex to prevent multiple crashing threads from trying to talk
- // to debuggerd at the same time.
- static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
- int ret = pthread_mutex_trylock(&crash_mutex);
- if (ret != 0) {
- if (ret == EBUSY) {
- __libc_format_log(ANDROID_LOG_INFO, "libc",
- "Another thread contacted debuggerd first; not contacting debuggerd.");
- // This will never complete since the lock is never released.
- pthread_mutex_lock(&crash_mutex);
- } else {
- __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_trylock failed: %s", strerror(ret));
+static bool check_dumpable(pid_t pid) {
+ // /proc/<pid> is owned by the effective UID of the process.
+ // Ownership of most of the other files in /proc/<pid> varies based on PR_SET_DUMPABLE.
+ // If PR_GET_DUMPABLE would return 0, they're owned by root, instead.
+ std::string proc_pid_path = android::base::StringPrintf("/proc/%d/", pid);
+ std::string proc_pid_status_path = proc_pid_path + "/status";
+
+ unique_fd proc_pid_fd(open(proc_pid_path.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC));
+ if (proc_pid_fd == -1) {
+ return false;
+ }
+ unique_fd proc_pid_status_fd(openat(proc_pid_fd, "status", O_RDONLY | O_CLOEXEC));
+ if (proc_pid_status_fd == -1) {
+ return false;
+ }
+
+ struct stat proc_pid_st;
+ struct stat proc_pid_status_st;
+ if (fstat(proc_pid_fd.get(), &proc_pid_st) != 0 ||
+ fstat(proc_pid_status_fd.get(), &proc_pid_status_st) != 0) {
+ return false;
+ }
+
+ // We can't figure out if a process is dumpable if its effective UID is root, but that's fine
+ // because being root bypasses the PR_SET_DUMPABLE check for ptrace.
+ if (proc_pid_st.st_uid == 0) {
+ return true;
+ }
+
+ if (proc_pid_status_st.st_uid == 0) {
+ return false;
+ }
+
+ return true;
+}
+
+bool debuggerd_trigger_dump(pid_t pid, unique_fd output_fd, DebuggerdDumpType dump_type,
+ int timeout_ms) {
+ LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
+ unique_fd sockfd;
+ const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
+ auto set_timeout = [timeout_ms, &sockfd, &end]() {
+ if (timeout_ms <= 0) {
+ return true;
}
- return;
- }
- int s = socket_abstract_client(SOCKET_NAME, SOCK_STREAM | SOCK_CLOEXEC);
- if (s == -1) {
- __libc_format_log(ANDROID_LOG_FATAL, "libc", "Unable to open connection to debuggerd: %s",
- strerror(errno));
- return;
- }
-
- // debuggerd knows our pid from the credentials on the
- // local socket but we need to tell it the tid of the crashing thread.
- // debuggerd will be paranoid and verify that we sent a tid
- // that's actually in our process.
- debugger_msg_t msg;
- msg.action = DEBUGGER_ACTION_CRASH;
- msg.tid = crashing_tid;
- msg.ignore_tid = pseudothread_tid;
- msg.abort_msg_address = 0;
-
- if (g_callbacks.get_abort_message) {
- msg.abort_msg_address = reinterpret_cast<uintptr_t>(g_callbacks.get_abort_message());
- }
-
- ret = TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg)));
- if (ret == sizeof(msg)) {
- char debuggerd_ack;
- ret = TEMP_FAILURE_RETRY(read(s, &debuggerd_ack, 1));
- if (g_callbacks.post_dump) {
- g_callbacks.post_dump();
+ auto now = std::chrono::steady_clock::now();
+ if (now > end) {
+ return false;
}
- } else {
- // read or write failed -- broken connection?
- __libc_format_log(ANDROID_LOG_FATAL, "libc", "Failed while talking to debuggerd: %s",
- strerror(errno));
- }
- close(s);
-}
+ auto time_left = std::chrono::duration_cast<std::chrono::microseconds>(end - now);
+ auto seconds = std::chrono::duration_cast<std::chrono::seconds>(time_left);
+ auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(time_left - seconds);
+ struct timeval timeout = {
+ .tv_sec = static_cast<long>(seconds.count()),
+ .tv_usec = static_cast<long>(microseconds.count()),
+ };
-struct debugger_thread_info {
- pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
- pid_t crashing_tid;
- pid_t pseudothread_tid;
- int signal_number;
- siginfo_t* info;
-};
+ if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0) {
+ return false;
+ }
+ if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) != 0) {
+ return false;
+ }
-// Logging and contacting debuggerd requires free file descriptors, which we might not have.
-// Work around this by spawning a "thread" that shares its parent's address space, but not its file
-// descriptor table, so that we can close random file descriptors without affecting the original
-// process. Note that this doesn't go through pthread_create, so TLS is shared with the spawning
-// process.
-static void* pseudothread_stack;
-static int debuggerd_dispatch_pseudothread(void* arg) {
- debugger_thread_info* thread_info = static_cast<debugger_thread_info*>(arg);
-
- for (int i = 3; i < 1024; ++i) {
- close(i);
- }
-
- log_signal_summary(thread_info->signal_number, thread_info->info);
- send_debuggerd_packet(thread_info->crashing_tid, thread_info->pseudothread_tid);
- pthread_mutex_unlock(&thread_info->mutex);
- return 0;
-}
-
-/*
- * Catches fatal signals so we can ask debuggerd to ptrace us before
- * we crash.
- */
-static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) {
- // It's possible somebody cleared the SA_SIGINFO flag, which would mean
- // our "info" arg holds an undefined value.
- if (!have_siginfo(signal_number)) {
- info = nullptr;
- }
-
- debugger_thread_info thread_info = {
- .crashing_tid = gettid(),
- .signal_number = signal_number,
- .info = info
+ return true;
};
- pthread_mutex_lock(&thread_info.mutex);
- pid_t child_pid = clone(debuggerd_dispatch_pseudothread, pseudothread_stack,
- CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_SETTID,
- &thread_info, nullptr, nullptr, &thread_info.pseudothread_tid);
-
- if (child_pid == -1) {
- fatal("failed to spawn debuggerd dispatch thread: %s", strerror(errno));
+ if (!check_dumpable(pid)) {
+ dprintf(output_fd.get(), "target pid %d is not dumpable\n", pid);
+ return true;
}
- // Wait for the child to finish and unlock the mutex.
- // This relies on bionic behavior that isn't guaranteed by the standard.
- pthread_mutex_lock(&thread_info.mutex);
-
-
- // We need to return from the signal handler so that debuggerd can dump the
- // thread that crashed, but returning here does not guarantee that the signal
- // will be thrown again, even for SIGSEGV and friends, since the signal could
- // have been sent manually. Resend the signal with rt_tgsigqueueinfo(2) to
- // preserve the SA_SIGINFO contents.
- signal(signal_number, SIG_DFL);
-
- struct siginfo si;
- if (!info) {
- memset(&si, 0, sizeof(si));
- si.si_code = SI_USER;
- si.si_pid = getpid();
- si.si_uid = getuid();
- info = &si;
- } else if (info->si_code >= 0 || info->si_code == SI_TKILL) {
- // rt_tgsigqueueinfo(2)'s documentation appears to be incorrect on kernels
- // that contain commit 66dd34a (3.9+). The manpage claims to only allow
- // negative si_code values that are not SI_TKILL, but 66dd34a changed the
- // check to allow all si_code values in calls coming from inside the house.
+ sockfd.reset(socket(AF_LOCAL, SOCK_SEQPACKET, 0));
+ if (sockfd == -1) {
+ PLOG(ERROR) << "libdebugger_client: failed to create socket";
+ return false;
}
- int rc = syscall(SYS_rt_tgsigqueueinfo, getpid(), gettid(), signal_number, info);
- if (rc != 0) {
- fatal("failed to resend signal during crash: %s", strerror(errno));
+ if (!set_timeout()) {
+ PLOG(ERROR) << "libdebugger_client: failed to set timeout";
+ return false;
}
+
+ if (socket_local_client_connect(sockfd.get(), kTombstonedInterceptSocketName,
+ ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET) == -1) {
+ PLOG(ERROR) << "libdebuggerd_client: failed to connect to tombstoned";
+ return false;
+ }
+
+ InterceptRequest req = {.pid = pid };
+ if (!set_timeout()) {
+ PLOG(ERROR) << "libdebugger_client: failed to set timeout";
+ }
+
+ if (send_fd(sockfd.get(), &req, sizeof(req), std::move(output_fd)) != sizeof(req)) {
+ PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
+ return false;
+ }
+
+ bool backtrace = dump_type == kDebuggerdBacktrace;
+ send_signal(pid, backtrace);
+
+ if (!set_timeout()) {
+ PLOG(ERROR) << "libdebugger_client: failed to set timeout";
+ }
+
+ InterceptResponse response;
+ ssize_t rc = TEMP_FAILURE_RETRY(recv(sockfd.get(), &response, sizeof(response), MSG_TRUNC));
+ if (rc == 0) {
+ LOG(ERROR) << "libdebuggerd_client: failed to read response from tombstoned: timeout reached?";
+ return false;
+ } else if (rc != sizeof(response)) {
+ LOG(ERROR)
+ << "libdebuggerd_client: received packet of unexpected length from tombstoned: expected "
+ << sizeof(response) << ", received " << rc;
+ return false;
+ }
+
+ if (response.success != 1) {
+ response.error_message[sizeof(response.error_message) - 1] = '\0';
+ LOG(ERROR) << "libdebuggerd_client: tombstoned reported failure: " << response.error_message;
+ }
+
+ LOG(INFO) << "libdebuggerd_client: done dumping process " << pid;
+
+ return true;
}
-void debuggerd_init(debuggerd_callbacks_t* callbacks) {
- if (callbacks) {
- g_callbacks = *callbacks;
+int dump_backtrace_to_file(pid_t tid, int fd) {
+ return dump_backtrace_to_file_timeout(tid, fd, 0);
+}
+
+int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs) {
+ android::base::unique_fd copy(dup(fd));
+ if (copy == -1) {
+ return -1;
}
-
- void* thread_stack_allocation =
- mmap(nullptr, PAGE_SIZE * 3, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
- if (thread_stack_allocation == MAP_FAILED) {
- fatal("failed to allocate debuggerd thread stack");
- }
-
- char* stack = static_cast<char*>(thread_stack_allocation) + PAGE_SIZE;
- if (mprotect(stack, PAGE_SIZE, PROT_READ | PROT_WRITE) != 0) {
- fatal("failed to mprotect debuggerd thread stack");
- }
-
- // Stack grows negatively, set it to the last byte in the page...
- stack = (stack + PAGE_SIZE - 1);
- // and align it.
- stack -= 15;
- pseudothread_stack = stack;
-
- struct sigaction action;
- memset(&action, 0, sizeof(action));
- sigemptyset(&action.sa_mask);
- action.sa_sigaction = debuggerd_signal_handler;
- action.sa_flags = SA_RESTART | SA_SIGINFO;
-
- // Use the alternate signal stack if available so we can catch stack overflows.
- action.sa_flags |= SA_ONSTACK;
-
- sigaction(SIGABRT, &action, nullptr);
- sigaction(SIGBUS, &action, nullptr);
- sigaction(SIGFPE, &action, nullptr);
- sigaction(SIGILL, &action, nullptr);
- sigaction(SIGSEGV, &action, nullptr);
-#if defined(SIGSTKFLT)
- sigaction(SIGSTKFLT, &action, nullptr);
-#endif
- sigaction(SIGTRAP, &action, nullptr);
+ int timeout_ms = timeout_secs > 0 ? timeout_secs * 1000 : 0;
+ return debuggerd_trigger_dump(tid, std::move(copy), kDebuggerdBacktrace, timeout_ms) ? 0 : -1;
}
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
new file mode 100644
index 0000000..b9dfedb
--- /dev/null
+++ b/debuggerd/crash_dump.cpp
@@ -0,0 +1,388 @@
+/*
+ * Copyright 2016, 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 <arpa/inet.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <syscall.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <limits>
+#include <memory>
+#include <set>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+#include <log/logger.h>
+#include <procinfo/process.h>
+#include <selinux/selinux.h>
+
+#include "backtrace.h"
+#include "tombstone.h"
+#include "utility.h"
+
+#include "debuggerd/handler.h"
+#include "debuggerd/protocol.h"
+#include "debuggerd/util.h"
+
+using android::base::unique_fd;
+using android::base::StringPrintf;
+
+static bool pid_contains_tid(pid_t pid, pid_t tid) {
+ std::string task_path = StringPrintf("/proc/%d/task/%d", pid, tid);
+ return access(task_path.c_str(), F_OK) == 0;
+}
+
+// Attach to a thread, and verify that it's still a member of the given process
+static bool ptrace_attach_thread(pid_t pid, pid_t tid) {
+ if (ptrace(PTRACE_ATTACH, tid, 0, 0) != 0) {
+ return false;
+ }
+
+ // Make sure that the task we attached to is actually part of the pid we're dumping.
+ if (!pid_contains_tid(pid, tid)) {
+ if (ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
+ PLOG(FATAL) << "failed to detach from thread " << tid;
+ }
+ errno = ECHILD;
+ return false;
+ }
+ return true;
+}
+
+static bool activity_manager_notify(int pid, int signal, const std::string& amfd_data) {
+ android::base::unique_fd amfd(socket_local_client("/data/system/ndebugsocket", ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM));
+ if (amfd.get() == -1) {
+ PLOG(ERROR) << "unable to connect to activity manager";
+ return false;
+ }
+
+ struct timeval tv = {
+ .tv_sec = 1,
+ .tv_usec = 0,
+ };
+ if (setsockopt(amfd.get(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {
+ PLOG(ERROR) << "failed to set send timeout on activity manager socket";
+ return false;
+ }
+ tv.tv_sec = 3; // 3 seconds on handshake read
+ if (setsockopt(amfd.get(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
+ PLOG(ERROR) << "failed to set receive timeout on activity manager socket";
+ return false;
+ }
+
+ // Activity Manager protocol: binary 32-bit network-byte-order ints for the
+ // pid and signal number, followed by the raw text of the dump, culminating
+ // in a zero byte that marks end-of-data.
+ uint32_t datum = htonl(pid);
+ if (!android::base::WriteFully(amfd, &datum, 4)) {
+ PLOG(ERROR) << "AM pid write failed";
+ return false;
+ }
+ datum = htonl(signal);
+ if (!android::base::WriteFully(amfd, &datum, 4)) {
+ PLOG(ERROR) << "AM signal write failed";
+ return false;
+ }
+ if (!android::base::WriteFully(amfd, amfd_data.c_str(), amfd_data.size() + 1)) {
+ PLOG(ERROR) << "AM data write failed";
+ return false;
+ }
+
+ // 3 sec timeout reading the ack; we're fine if the read fails.
+ char ack;
+ android::base::ReadFully(amfd, &ack, 1);
+ return true;
+}
+
+static bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd) {
+ unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
+ ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+ if (sockfd == -1) {
+ PLOG(ERROR) << "failed to connect to tombstoned";
+ return false;
+ }
+
+ TombstonedCrashPacket packet = {};
+ packet.packet_type = CrashPacketType::kDumpRequest;
+ packet.packet.dump_request.pid = pid;
+ if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) {
+ PLOG(ERROR) << "failed to write DumpRequest packet";
+ return false;
+ }
+
+ unique_fd tmp_output_fd;
+ ssize_t rc = recv_fd(sockfd, &packet, sizeof(packet), &tmp_output_fd);
+ if (rc == -1) {
+ PLOG(ERROR) << "failed to read response to DumpRequest packet";
+ return false;
+ } else if (rc != sizeof(packet)) {
+ LOG(ERROR) << "read DumpRequest response packet of incorrect length (expected "
+ << sizeof(packet) << ", got " << rc << ")";
+ return false;
+ }
+
+ *tombstoned_socket = std::move(sockfd);
+ *output_fd = std::move(tmp_output_fd);
+ return true;
+}
+
+static bool tombstoned_notify_completion(int tombstoned_socket) {
+ TombstonedCrashPacket packet = {};
+ packet.packet_type = CrashPacketType::kCompletedDump;
+ if (TEMP_FAILURE_RETRY(write(tombstoned_socket, &packet, sizeof(packet))) != sizeof(packet)) {
+ return false;
+ }
+ return true;
+}
+
+static void abort_handler(pid_t target, const bool& tombstoned_connected,
+ unique_fd& tombstoned_socket, unique_fd& output_fd,
+ const char* abort_msg) {
+ LOG(ERROR) << abort_msg;
+
+ // If we abort before we get an output fd, contact tombstoned to let any
+ // potential listeners know that we failed.
+ if (!tombstoned_connected) {
+ if (!tombstoned_connect(target, &tombstoned_socket, &output_fd)) {
+ // We failed to connect, not much we can do.
+ LOG(ERROR) << "failed to connected to tombstoned to report failure";
+ _exit(1);
+ }
+ }
+
+ dprintf(output_fd.get(), "crash_dump failed to dump process %d: %s\n", target, abort_msg);
+
+ // Don't dump ourselves.
+ _exit(1);
+}
+
+static void check_process(int proc_fd, pid_t expected_pid) {
+ android::procinfo::ProcessInfo proc_info;
+ if (!android::procinfo::GetProcessInfoFromProcPidFd(proc_fd, &proc_info)) {
+ LOG(FATAL) << "failed to fetch process info";
+ }
+
+ if (proc_info.pid != expected_pid) {
+ LOG(FATAL) << "pid mismatch: expected " << expected_pid << ", actual " << proc_info.ppid;
+ }
+}
+
+int main(int argc, char** argv) {
+ pid_t target = getppid();
+ bool tombstoned_connected = false;
+ unique_fd tombstoned_socket;
+ unique_fd output_fd;
+
+ android::base::InitLogging(argv);
+ android::base::SetAborter([&](const char* abort_msg) {
+ abort_handler(target, tombstoned_connected, tombstoned_socket, output_fd, abort_msg);
+ });
+
+ if (argc != 2) {
+ return 1;
+ }
+
+ pid_t main_tid;
+
+ if (target == 1) {
+ LOG(FATAL) << "target died before we could attach";
+ }
+
+ if (!android::base::ParseInt(argv[1], &main_tid, 1, std::numeric_limits<pid_t>::max())) {
+ LOG(FATAL) << "invalid main tid: " << argv[1];
+ }
+
+ android::procinfo::ProcessInfo target_info;
+ if (!android::procinfo::GetProcessInfo(main_tid, &target_info)) {
+ LOG(FATAL) << "failed to fetch process info for target " << main_tid;
+ }
+
+ if (main_tid != target_info.tid || target != target_info.pid) {
+ LOG(FATAL) << "target info mismatch, expected pid " << target << ", tid " << main_tid
+ << ", received pid " << target_info.pid << ", tid " << target_info.tid;
+ }
+
+ // Open /proc/`getppid()` in the original process, and pass it down to the forked child.
+ std::string target_proc_path = "/proc/" + std::to_string(target);
+ int target_proc_fd = open(target_proc_path.c_str(), O_DIRECTORY | O_RDONLY);
+ if (target_proc_fd == -1) {
+ PLOG(FATAL) << "failed to open " << target_proc_path;
+ }
+
+ // Reparent ourselves to init, so that the signal handler can waitpid on the
+ // original process to avoid leaving a zombie for non-fatal dumps.
+ pid_t forkpid = fork();
+ if (forkpid == -1) {
+ PLOG(FATAL) << "fork failed";
+ } else if (forkpid != 0) {
+ exit(0);
+ }
+
+ check_process(target_proc_fd, target);
+
+ int attach_error = 0;
+ if (!ptrace_attach_thread(target, main_tid)) {
+ PLOG(FATAL) << "failed to attach to thread " << main_tid << " in process " << target;
+ }
+
+ check_process(target_proc_fd, target);
+
+ LOG(INFO) << "obtaining output fd from tombstoned";
+ tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd);
+
+ // Write a '\1' to stdout to tell the crashing process to resume.
+ if (TEMP_FAILURE_RETRY(write(STDOUT_FILENO, "\1", 1)) == -1) {
+ PLOG(ERROR) << "failed to communicate to target process";
+ }
+
+ if (tombstoned_connected) {
+ if (TEMP_FAILURE_RETRY(dup2(output_fd.get(), STDOUT_FILENO)) == -1) {
+ PLOG(ERROR) << "failed to dup2 output fd (" << output_fd.get() << ") to STDOUT_FILENO";
+ }
+ } else {
+ unique_fd devnull(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
+ TEMP_FAILURE_RETRY(dup2(devnull.get(), STDOUT_FILENO));
+ }
+
+ if (attach_error != 0) {
+ PLOG(FATAL) << "failed to attach to thread " << main_tid << " in process " << target;
+ }
+
+ LOG(INFO) << "performing dump of process " << target << " (target tid = " << main_tid << ")";
+
+ // At this point, the thread that made the request has been PTRACE_ATTACHed
+ // and has the signal that triggered things queued. Send PTRACE_CONT, and
+ // then wait for the signal.
+ if (ptrace(PTRACE_CONT, main_tid, 0, 0) != 0) {
+ PLOG(ERROR) << "PTRACE_CONT(" << main_tid << ") failed";
+ exit(1);
+ }
+
+ siginfo_t siginfo = {};
+ if (!wait_for_signal(main_tid, &siginfo)) {
+ printf("failed to wait for signal in tid %d: %s\n", main_tid, strerror(errno));
+ exit(1);
+ }
+
+ int signo = siginfo.si_signo;
+ bool backtrace = false;
+ uintptr_t abort_address = 0;
+
+ // si_value can represent three things:
+ // 0: dump tombstone
+ // 1: dump backtrace
+ // everything else: abort message address (implies dump tombstone)
+ if (siginfo.si_value.sival_int == 1) {
+ backtrace = true;
+ } else if (siginfo.si_value.sival_ptr != nullptr) {
+ abort_address = reinterpret_cast<uintptr_t>(siginfo.si_value.sival_ptr);
+ }
+
+ // Now that we have the signal that kicked things off, attach all of the
+ // sibling threads, and then proceed.
+ bool fatal_signal = signo != DEBUGGER_SIGNAL;
+ int resume_signal = fatal_signal ? signo : 0;
+ std::set<pid_t> siblings;
+ if (resume_signal == 0) {
+ if (!android::procinfo::GetProcessTids(target, &siblings)) {
+ PLOG(FATAL) << "failed to get process siblings";
+ }
+ siblings.erase(main_tid);
+
+ for (pid_t sibling_tid : siblings) {
+ if (!ptrace_attach_thread(target, sibling_tid)) {
+ PLOG(FATAL) << "failed to attach to thread " << main_tid << " in process " << target;
+ }
+ }
+ }
+
+ check_process(target_proc_fd, target);
+
+ // TODO: Use seccomp to lock ourselves down.
+
+ std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(main_tid));
+ std::string amfd_data;
+
+ if (backtrace) {
+ dump_backtrace(output_fd.get(), backtrace_map.get(), target, main_tid, siblings, 0);
+ } else {
+ // Collect the list of open files.
+ OpenFilesList open_files;
+ populate_open_files_list(target, &open_files);
+
+ engrave_tombstone(output_fd.get(), backtrace_map.get(), open_files, target, main_tid, siblings,
+ abort_address, fatal_signal ? &amfd_data : nullptr);
+ }
+
+ bool wait_for_gdb = android::base::GetBoolProperty("debug.debuggerd.wait_for_gdb", false);
+ if (wait_for_gdb) {
+ // Don't wait_for_gdb when the process didn't actually crash.
+ if (!fatal_signal) {
+ wait_for_gdb = false;
+ } else {
+ // Use ALOGI to line up with output from engrave_tombstone.
+ ALOGI(
+ "***********************************************************\n"
+ "* Process %d has been suspended while crashing.\n"
+ "* To attach gdbserver and start gdb, run this on the host:\n"
+ "*\n"
+ "* gdbclient.py -p %d\n"
+ "*\n"
+ "***********************************************************",
+ target, main_tid);
+ }
+ }
+
+ for (pid_t tid : siblings) {
+ // Don't send the signal to sibling threads.
+ if (ptrace(PTRACE_DETACH, tid, 0, wait_for_gdb ? SIGSTOP : 0) != 0) {
+ PLOG(ERROR) << "ptrace detach from " << tid << " failed";
+ }
+ }
+
+ if (ptrace(PTRACE_DETACH, main_tid, 0, wait_for_gdb ? SIGSTOP : resume_signal)) {
+ PLOG(ERROR) << "ptrace detach from main thread " << main_tid << " failed";
+ }
+
+ if (wait_for_gdb) {
+ if (tgkill(target, main_tid, resume_signal) != 0) {
+ PLOG(ERROR) << "failed to resend signal to process " << target;
+ }
+ }
+
+ if (fatal_signal) {
+ activity_manager_notify(target, signo, amfd_data);
+ }
+
+ // Close stdout before we notify tombstoned of completion.
+ close(STDOUT_FILENO);
+ if (!tombstoned_notify_completion(tombstoned_socket.get())) {
+ LOG(ERROR) << "failed to notify tombstoned of completion";
+ }
+
+ return 0;
+}
diff --git a/debuggerd/crasher/Android.mk b/debuggerd/crasher/Android.mk
new file mode 100644
index 0000000..b8b786b
--- /dev/null
+++ b/debuggerd/crasher/Android.mk
@@ -0,0 +1,66 @@
+LOCAL_PATH := $(call my-dir)
+
+crasher_cppflags := \
+ -std=gnu++14 \
+ -W \
+ -Wall \
+ -Wextra \
+ -Wunused \
+ -Werror \
+ -O0 \
+ -fstack-protector-all \
+ -Wno-free-nonheap-object \
+ -Wno-date-time
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := crasher.cpp
+LOCAL_SRC_FILES_arm := arm/crashglue.S
+LOCAL_SRC_FILES_arm64 := arm64/crashglue.S
+LOCAL_SRC_FILES_mips := mips/crashglue.S
+LOCAL_SRC_FILES_mips64 := mips64/crashglue.S
+LOCAL_SRC_FILES_x86 := x86/crashglue.S
+LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := optional
+LOCAL_CPPFLAGS := $(crasher_cppflags)
+LOCAL_SHARED_LIBRARIES := libbase liblog
+
+# The arm emulator has VFP but not VFPv3-D32.
+ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
+LOCAL_ASFLAGS_arm += -DHAS_VFP_D32
+endif
+
+LOCAL_MODULE := crasher
+LOCAL_MODULE_STEM_32 := crasher
+LOCAL_MODULE_STEM_64 := crasher64
+LOCAL_MULTILIB := both
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := crasher.cpp
+LOCAL_SRC_FILES_arm := arm/crashglue.S
+LOCAL_SRC_FILES_arm64 := arm64/crashglue.S
+LOCAL_SRC_FILES_mips := mips/crashglue.S
+LOCAL_SRC_FILES_mips64 := mips64/crashglue.S
+LOCAL_SRC_FILES_x86 := x86/crashglue.S
+LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := optional
+LOCAL_CPPFLAGS := $(crasher_cppflags) -DSTATIC_CRASHER
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_SHARED_LIBRARIES := libbase liblog
+
+# The arm emulator has VFP but not VFPv3-D32.
+ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
+LOCAL_ASFLAGS_arm += -DHAS_VFP_D32
+endif
+
+LOCAL_MODULE := static_crasher
+LOCAL_MODULE_STEM_32 := static_crasher
+LOCAL_MODULE_STEM_64 := static_crasher64
+LOCAL_MULTILIB := both
+
+LOCAL_STATIC_LIBRARIES := libdebuggerd_handler libbase liblog
+
+include $(BUILD_EXECUTABLE)
diff --git a/debuggerd/arm/crashglue.S b/debuggerd/crasher/arm/crashglue.S
similarity index 100%
rename from debuggerd/arm/crashglue.S
rename to debuggerd/crasher/arm/crashglue.S
diff --git a/debuggerd/arm64/crashglue.S b/debuggerd/crasher/arm64/crashglue.S
similarity index 100%
rename from debuggerd/arm64/crashglue.S
rename to debuggerd/crasher/arm64/crashglue.S
diff --git a/debuggerd/crasher.cpp b/debuggerd/crasher/crasher.cpp
similarity index 99%
rename from debuggerd/crasher.cpp
rename to debuggerd/crasher/crasher.cpp
index e650f22..288f116 100644
--- a/debuggerd/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -29,11 +29,11 @@
#include <unistd.h>
// We test both kinds of logging.
-#include <android/log.h>
#include <android-base/logging.h>
+#include <log/log.h>
#if defined(STATIC_CRASHER)
-#include "debuggerd/client.h"
+#include "debuggerd/handler.h"
#endif
#define noinline __attribute__((__noinline__))
diff --git a/debuggerd/mips/crashglue.S b/debuggerd/crasher/mips/crashglue.S
similarity index 100%
rename from debuggerd/mips/crashglue.S
rename to debuggerd/crasher/mips/crashglue.S
diff --git a/debuggerd/mips64/crashglue.S b/debuggerd/crasher/mips64/crashglue.S
similarity index 100%
rename from debuggerd/mips64/crashglue.S
rename to debuggerd/crasher/mips64/crashglue.S
diff --git a/debuggerd/x86/crashglue.S b/debuggerd/crasher/x86/crashglue.S
similarity index 100%
rename from debuggerd/x86/crashglue.S
rename to debuggerd/crasher/x86/crashglue.S
diff --git a/debuggerd/x86_64/crashglue.S b/debuggerd/crasher/x86_64/crashglue.S
similarity index 100%
rename from debuggerd/x86_64/crashglue.S
rename to debuggerd/crasher/x86_64/crashglue.S
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 9b82f64..3208230 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2006, The Android Open Source Project
+ * Copyright 2016, 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.
@@ -14,937 +14,68 @@
* limitations under the License.
*/
-#define LOG_TAG "debuggerd"
-
-#include <arpa/inet.h>
-#include <dirent.h>
-#include <elf.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/input.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdarg.h>
+#include <err.h>
#include <stdio.h>
-#include <sys/poll.h>
-#include <sys/prctl.h>
-#include <sys/ptrace.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/un.h>
-#include <time.h>
+#include <stdlib.h>
+#include <string.h>
-#include <memory>
-#include <set>
-#include <string>
+#include <limits>
+#include <thread>
-#include <selinux/android.h>
-
-#include <android/log.h>
#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
#include <android-base/unique_fd.h>
-#include <cutils/debugger.h>
-#include <cutils/properties.h>
-#include <cutils/sockets.h>
-
-#include <private/android_filesystem_config.h>
-
#include <debuggerd/client.h>
+#include <debuggerd/util.h>
+#include <selinux/selinux.h>
-#include "backtrace.h"
-#include "getevent.h"
-#include "open_files_list.h"
-#include "signal_sender.h"
-#include "tombstone.h"
-#include "utility.h"
+using android::base::unique_fd;
-// If the 32 bit executable is compiled on a 64 bit system,
-// use the 32 bit socket name.
-#if defined(TARGET_IS_64_BIT) && !defined(__LP64__)
-#define SOCKET_NAME DEBUGGER32_SOCKET_NAME
-#else
-#define SOCKET_NAME DEBUGGER_SOCKET_NAME
-#endif
+static void usage(int exit_code) {
+ fprintf(stderr, "usage: debuggerd [-b] PID\n");
+ _exit(exit_code);
+}
-struct debugger_request_t {
- debugger_action_t action;
- pid_t pid, tid;
- uid_t uid, gid;
- pid_t ignore_tid;
- uintptr_t abort_msg_address;
-};
+static std::thread spawn_redirect_thread(unique_fd fd) {
+ return std::thread([fd{ std::move(fd) }]() {
+ while (true) {
+ char buf[BUFSIZ];
+ ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), buf, sizeof(buf)));
+ if (rc <= 0) {
+ return;
+ }
-static void wait_for_user_action(const debugger_request_t& request) {
- // Explain how to attach the debugger.
- ALOGI("***********************************************************\n"
- "* Process %d has been suspended while crashing.\n"
- "* To attach gdbserver and start gdb, run this on the host:\n"
- "*\n"
- "* gdbclient.py -p %d\n"
- "*\n"
- "* Wait for gdb to start, then press the VOLUME DOWN key\n"
- "* to let the process continue crashing.\n"
- "***********************************************************",
- request.pid, request.tid);
-
- // Wait for VOLUME DOWN.
- while (true) {
- input_event e;
- if (get_event(&e, -1) == 0) {
- if (e.type == EV_KEY && e.code == KEY_VOLUMEDOWN && e.value == 0) {
- break;
+ if (!android::base::WriteFully(STDOUT_FILENO, buf, rc)) {
+ return;
}
}
- }
-
- ALOGI("debuggerd resuming process %d", request.pid);
+ });
}
-static int get_process_info(pid_t tid, pid_t* out_pid, uid_t* out_uid, uid_t* out_gid) {
- char path[64];
- snprintf(path, sizeof(path), "/proc/%d/status", tid);
+int main(int argc, char* argv[]) {
+ if (argc <= 1) usage(0);
+ if (argc > 3) usage(1);
+ if (argc == 3 && strcmp(argv[1], "-b") != 0) usage(1);
- FILE* fp = fopen(path, "r");
- if (!fp) {
- return -1;
+ pid_t pid;
+ if (!android::base::ParseInt(argv[argc - 1], &pid, 1, std::numeric_limits<pid_t>::max())) {
+ usage(1);
}
- int fields = 0;
- char line[1024];
- while (fgets(line, sizeof(line), fp)) {
- size_t len = strlen(line);
- if (len > 6 && !memcmp(line, "Tgid:\t", 6)) {
- *out_pid = atoi(line + 6);
- fields |= 1;
- } else if (len > 5 && !memcmp(line, "Uid:\t", 5)) {
- *out_uid = atoi(line + 5);
- fields |= 2;
- } else if (len > 5 && !memcmp(line, "Gid:\t", 5)) {
- *out_gid = atoi(line + 5);
- fields |= 4;
- }
- }
- fclose(fp);
- return fields == 7 ? 0 : -1;
-}
-
-/*
- * Corresponds with debugger_action_t enum type in
- * include/cutils/debugger.h.
- */
-static const char *debuggerd_perms[] = {
- NULL, /* crash is only used on self, no check applied */
- "dump_tombstone",
- "dump_backtrace"
-};
-
-static int audit_callback(void* data, security_class_t /* cls */, char* buf, size_t len)
-{
- struct debugger_request_t* req = reinterpret_cast<debugger_request_t*>(data);
-
- if (!req) {
- ALOGE("No debuggerd request audit data");
- return 0;
- }
-
- snprintf(buf, len, "pid=%d uid=%d gid=%d", req->pid, req->uid, req->gid);
- return 0;
-}
-
-static bool selinux_action_allowed(int s, debugger_request_t* request)
-{
- char *scon = NULL, *tcon = NULL;
- const char *tclass = "debuggerd";
- const char *perm;
- bool allowed = false;
-
- if (request->action <= 0 || request->action >= (sizeof(debuggerd_perms)/sizeof(debuggerd_perms[0]))) {
- ALOGE("SELinux: No permission defined for debugger action %d", request->action);
- return false;
+ unique_fd piperead, pipewrite;
+ if (!Pipe(&piperead, &pipewrite)) {
+ err(1, "failed to create pipe");
}
- perm = debuggerd_perms[request->action];
-
- if (getpeercon(s, &scon) < 0) {
- ALOGE("Cannot get peer context from socket\n");
- goto out;
+ std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
+ bool backtrace = argc == 3;
+ if (!debuggerd_trigger_dump(pid, std::move(pipewrite),
+ backtrace ? kDebuggerdBacktrace : kDebuggerdBacktrace, 0)) {
+ redirect_thread.join();
+ errx(1, "failed to dump process %d", pid);
}
- if (getpidcon(request->tid, &tcon) < 0) {
- ALOGE("Cannot get context for tid %d\n", request->tid);
- goto out;
- }
-
- allowed = (selinux_check_access(scon, tcon, tclass, perm, reinterpret_cast<void*>(request)) == 0);
-
-out:
- freecon(scon);
- freecon(tcon);
- return allowed;
-}
-
-static bool pid_contains_tid(pid_t pid, pid_t tid) {
- char task_path[PATH_MAX];
- if (snprintf(task_path, PATH_MAX, "/proc/%d/task/%d", pid, tid) >= PATH_MAX) {
- ALOGE("debuggerd: task path overflow (pid = %d, tid = %d)\n", pid, tid);
- exit(1);
- }
-
- return access(task_path, F_OK) == 0;
-}
-
-static int read_request(int fd, debugger_request_t* out_request) {
- ucred cr;
- socklen_t len = sizeof(cr);
- int status = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
- if (status != 0) {
- ALOGE("cannot get credentials");
- return -1;
- }
-
- ALOGV("reading tid");
- pollfd pollfds[1];
- pollfds[0].fd = fd;
- pollfds[0].events = POLLIN;
- pollfds[0].revents = 0;
- status = TEMP_FAILURE_RETRY(poll(pollfds, 1, 3000));
- if (status != 1) {
- ALOGE("timed out reading tid (from pid=%d uid=%d)\n", cr.pid, cr.uid);
- return -1;
- }
-
- debugger_msg_t msg;
- memset(&msg, 0, sizeof(msg));
- status = TEMP_FAILURE_RETRY(read(fd, &msg, sizeof(msg)));
- if (status < 0) {
- ALOGE("read failure? %s (pid=%d uid=%d)\n", strerror(errno), cr.pid, cr.uid);
- return -1;
- }
- if (status != sizeof(debugger_msg_t)) {
- ALOGE("invalid crash request of size %d (from pid=%d uid=%d)\n", status, cr.pid, cr.uid);
- return -1;
- }
-
- out_request->action = static_cast<debugger_action_t>(msg.action);
- out_request->tid = msg.tid;
- out_request->ignore_tid = msg.ignore_tid;
- out_request->pid = cr.pid;
- out_request->uid = cr.uid;
- out_request->gid = cr.gid;
- out_request->abort_msg_address = msg.abort_msg_address;
-
- if (msg.action == DEBUGGER_ACTION_CRASH) {
- // Ensure that the tid reported by the crashing process is valid.
- // This check needs to happen again after ptracing the requested thread to prevent a race.
- if (!pid_contains_tid(out_request->pid, out_request->tid)) {
- ALOGE("tid %d does not exist in pid %d. ignoring debug request\n", out_request->tid,
- out_request->pid);
- return -1;
- }
- } else if (cr.uid == 0 || (cr.uid == AID_SYSTEM && msg.action == DEBUGGER_ACTION_DUMP_BACKTRACE)) {
- // Only root or system can ask us to attach to any process and dump it explicitly.
- // However, system is only allowed to collect backtraces but cannot dump tombstones.
- status = get_process_info(out_request->tid, &out_request->pid,
- &out_request->uid, &out_request->gid);
- if (status < 0) {
- ALOGE("tid %d does not exist. ignoring explicit dump request\n", out_request->tid);
- return -1;
- }
-
- if (!selinux_action_allowed(fd, out_request))
- return -1;
- } else {
- // No one else is allowed to dump arbitrary processes.
- return -1;
- }
+ redirect_thread.join();
return 0;
}
-
-static int activity_manager_connect() {
- android::base::unique_fd amfd(socket(PF_UNIX, SOCK_STREAM, 0));
- if (amfd.get() < -1) {
- ALOGE("debuggerd: Unable to connect to activity manager (socket failed: %s)", strerror(errno));
- return -1;
- }
-
- struct sockaddr_un address;
- memset(&address, 0, sizeof(address));
- address.sun_family = AF_UNIX;
- // The path used here must match the value defined in NativeCrashListener.java.
- strncpy(address.sun_path, "/data/system/ndebugsocket", sizeof(address.sun_path));
- if (TEMP_FAILURE_RETRY(connect(amfd.get(), reinterpret_cast<struct sockaddr*>(&address),
- sizeof(address))) == -1) {
- ALOGE("debuggerd: Unable to connect to activity manager (connect failed: %s)", strerror(errno));
- return -1;
- }
-
- struct timeval tv;
- memset(&tv, 0, sizeof(tv));
- tv.tv_sec = 1; // tight leash
- if (setsockopt(amfd.get(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {
- ALOGE("debuggerd: Unable to connect to activity manager (setsockopt SO_SNDTIMEO failed: %s)",
- strerror(errno));
- return -1;
- }
-
- tv.tv_sec = 3; // 3 seconds on handshake read
- if (setsockopt(amfd.get(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
- ALOGE("debuggerd: Unable to connect to activity manager (setsockopt SO_RCVTIMEO failed: %s)",
- strerror(errno));
- return -1;
- }
-
- return amfd.release();
-}
-
-static void activity_manager_write(int pid, int signal, int amfd, const std::string& amfd_data) {
- if (amfd == -1) {
- return;
- }
-
- // Activity Manager protocol: binary 32-bit network-byte-order ints for the
- // pid and signal number, followed by the raw text of the dump, culminating
- // in a zero byte that marks end-of-data.
- uint32_t datum = htonl(pid);
- if (!android::base::WriteFully(amfd, &datum, 4)) {
- ALOGE("AM pid write failed: %s\n", strerror(errno));
- return;
- }
- datum = htonl(signal);
- if (!android::base::WriteFully(amfd, &datum, 4)) {
- ALOGE("AM signal write failed: %s\n", strerror(errno));
- return;
- }
-
- if (!android::base::WriteFully(amfd, amfd_data.c_str(), amfd_data.size())) {
- ALOGE("AM data write failed: %s\n", strerror(errno));
- return;
- }
-
- // Send EOD to the Activity Manager, then wait for its ack to avoid racing
- // ahead and killing the target out from under it.
- uint8_t eodMarker = 0;
- if (!android::base::WriteFully(amfd, &eodMarker, 1)) {
- ALOGE("AM eod write failed: %s\n", strerror(errno));
- return;
- }
- // 3 sec timeout reading the ack; we're fine if the read fails.
- android::base::ReadFully(amfd, &eodMarker, 1);
-}
-
-static bool should_attach_gdb(const debugger_request_t& request) {
- if (request.action == DEBUGGER_ACTION_CRASH) {
- return property_get_bool("debug.debuggerd.wait_for_gdb", false);
- }
- return false;
-}
-
-#if defined(__LP64__)
-static bool is32bit(pid_t tid) {
- char* exeline;
- if (asprintf(&exeline, "/proc/%d/exe", tid) == -1) {
- return false;
- }
- int fd = TEMP_FAILURE_RETRY(open(exeline, O_RDONLY | O_CLOEXEC));
- int saved_errno = errno;
- free(exeline);
- if (fd == -1) {
- ALOGW("Failed to open /proc/%d/exe %s", tid, strerror(saved_errno));
- return false;
- }
-
- char ehdr[EI_NIDENT];
- ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &ehdr, sizeof(ehdr)));
- close(fd);
- if (bytes != (ssize_t) sizeof(ehdr) || memcmp(ELFMAG, ehdr, SELFMAG) != 0) {
- return false;
- }
- if (ehdr[EI_CLASS] == ELFCLASS32) {
- return true;
- }
- return false;
-}
-
-static void redirect_to_32(int fd, debugger_request_t* request) {
- debugger_msg_t msg;
- memset(&msg, 0, sizeof(msg));
- msg.tid = request->tid;
- msg.action = request->action;
-
- int sock_fd = socket_local_client(DEBUGGER32_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
- SOCK_STREAM | SOCK_CLOEXEC);
- if (sock_fd < 0) {
- ALOGE("Failed to connect to debuggerd32: %s", strerror(errno));
- return;
- }
-
- if (TEMP_FAILURE_RETRY(write(sock_fd, &msg, sizeof(msg))) != (ssize_t) sizeof(msg)) {
- ALOGE("Failed to write request to debuggerd32 socket: %s", strerror(errno));
- close(sock_fd);
- return;
- }
-
- char ack;
- if (TEMP_FAILURE_RETRY(read(sock_fd, &ack, 1)) == -1) {
- ALOGE("Failed to read ack from debuggerd32 socket: %s", strerror(errno));
- close(sock_fd);
- return;
- }
-
- char buffer[1024];
- ssize_t bytes_read;
- while ((bytes_read = TEMP_FAILURE_RETRY(read(sock_fd, buffer, sizeof(buffer)))) > 0) {
- ssize_t bytes_to_send = bytes_read;
- ssize_t bytes_written;
- do {
- bytes_written = TEMP_FAILURE_RETRY(write(fd, buffer + bytes_read - bytes_to_send,
- bytes_to_send));
- if (bytes_written == -1) {
- if (errno == EAGAIN) {
- // Retry the write.
- continue;
- }
- ALOGE("Error while writing data to fd: %s", strerror(errno));
- break;
- }
- bytes_to_send -= bytes_written;
- } while (bytes_written != 0 && bytes_to_send > 0);
- if (bytes_to_send != 0) {
- ALOGE("Failed to write all data to fd: read %zd, sent %zd", bytes_read, bytes_to_send);
- break;
- }
- }
- close(sock_fd);
-}
-#endif
-
-// Attach to a thread, and verify that it's still a member of the given process
-static bool ptrace_attach_thread(pid_t pid, pid_t tid) {
- if (ptrace(PTRACE_ATTACH, tid, 0, 0) != 0) {
- return false;
- }
-
- // Make sure that the task we attached to is actually part of the pid we're dumping.
- if (!pid_contains_tid(pid, tid)) {
- if (ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
- ALOGE("debuggerd: failed to detach from thread '%d'", tid);
- exit(1);
- }
- return false;
- }
-
- return true;
-}
-
-static void ptrace_siblings(pid_t pid, pid_t main_tid, pid_t ignore_tid, std::set<pid_t>& tids) {
- char task_path[PATH_MAX];
-
- if (snprintf(task_path, PATH_MAX, "/proc/%d/task", pid) >= PATH_MAX) {
- ALOGE("debuggerd: task path overflow (pid = %d)\n", pid);
- abort();
- }
-
- std::unique_ptr<DIR, int (*)(DIR*)> d(opendir(task_path), closedir);
-
- // Bail early if the task directory cannot be opened.
- if (!d) {
- ALOGE("debuggerd: failed to open /proc/%d/task: %s", pid, strerror(errno));
- return;
- }
-
- struct dirent* de;
- while ((de = readdir(d.get())) != NULL) {
- // Ignore "." and "..".
- if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
- continue;
- }
-
- char* end;
- pid_t tid = strtoul(de->d_name, &end, 10);
- if (*end) {
- continue;
- }
-
- if (tid == main_tid || tid == ignore_tid) {
- continue;
- }
-
- if (!ptrace_attach_thread(pid, tid)) {
- ALOGE("debuggerd: ptrace attach to %d failed: %s", tid, strerror(errno));
- continue;
- }
-
- tids.insert(tid);
- }
-}
-
-static bool perform_dump(const debugger_request_t& request, int fd, int tombstone_fd,
- BacktraceMap* backtrace_map, const OpenFilesList& open_files,
- const std::set<pid_t>& siblings,
- int* crash_signal, std::string* amfd_data) {
- if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) {
- ALOGE("debuggerd: failed to respond to client: %s\n", strerror(errno));
- return false;
- }
-
- while (true) {
- // wait_for_signal waits for forever, but the watchdog process will kill us
- // if it takes too long.
- int signal = wait_for_signal(request.tid);
- switch (signal) {
- case -1:
- ALOGE("debuggerd: timed out waiting for signal");
- return false;
-
- case SIGSTOP:
- if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
- ALOGV("debuggerd: stopped -- dumping to tombstone");
- engrave_tombstone(tombstone_fd, backtrace_map, open_files,
- request.pid, request.tid, siblings,
- request.abort_msg_address, amfd_data);
- } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) {
- ALOGV("debuggerd: stopped -- dumping to fd");
- dump_backtrace(fd, backtrace_map, request.pid, request.tid, siblings, nullptr);
- } else {
- ALOGV("debuggerd: stopped -- continuing");
- if (ptrace(PTRACE_CONT, request.tid, 0, 0) != 0) {
- ALOGE("debuggerd: ptrace continue failed: %s", strerror(errno));
- return false;
- }
- continue; // loop again
- }
- break;
-
- case SIGABRT:
- case SIGBUS:
- case SIGFPE:
- case SIGILL:
- case SIGSEGV:
-#ifdef SIGSTKFLT
- case SIGSTKFLT:
-#endif
- case SIGSYS:
- case SIGTRAP:
- ALOGV("stopped -- fatal signal\n");
- *crash_signal = signal;
- engrave_tombstone(tombstone_fd, backtrace_map, open_files,
- request.pid, request.tid, siblings,
- request.abort_msg_address, amfd_data);
- break;
-
- default:
- ALOGE("debuggerd: process stopped due to unexpected signal %d\n", signal);
- break;
- }
- break;
- }
-
- return true;
-}
-
-static bool drop_privileges() {
- // AID_LOG: for reading the logs data associated with the crashing process.
- // AID_READPROC: for reading /proc/<PID>/{comm,cmdline}.
- gid_t groups[] = { AID_DEBUGGERD, AID_LOG, AID_READPROC };
- if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
- ALOGE("debuggerd: failed to setgroups: %s", strerror(errno));
- return false;
- }
-
- if (setresgid(AID_DEBUGGERD, AID_DEBUGGERD, AID_DEBUGGERD) != 0) {
- ALOGE("debuggerd: failed to setresgid: %s", strerror(errno));
- return false;
- }
-
- if (setresuid(AID_DEBUGGERD, AID_DEBUGGERD, AID_DEBUGGERD) != 0) {
- ALOGE("debuggerd: failed to setresuid: %s", strerror(errno));
- return false;
- }
-
- return true;
-}
-
-static void worker_process(int fd, debugger_request_t& request) {
- // Open the tombstone file if we need it.
- std::string tombstone_path;
- int tombstone_fd = -1;
- switch (request.action) {
- case DEBUGGER_ACTION_DUMP_TOMBSTONE:
- case DEBUGGER_ACTION_CRASH:
- tombstone_fd = open_tombstone(&tombstone_path);
- if (tombstone_fd == -1) {
- ALOGE("debuggerd: failed to open tombstone file: %s\n", strerror(errno));
- exit(1);
- }
- break;
-
- case DEBUGGER_ACTION_DUMP_BACKTRACE:
- break;
-
- default:
- ALOGE("debuggerd: unexpected request action: %d", request.action);
- exit(1);
- }
-
- // At this point, the thread that made the request is blocked in
- // a read() call. If the thread has crashed, then this gives us
- // time to PTRACE_ATTACH to it before it has a chance to really fault.
- //
- // The PTRACE_ATTACH sends a SIGSTOP to the target process, but it
- // won't necessarily have stopped by the time ptrace() returns. (We
- // currently assume it does.) We write to the file descriptor to
- // ensure that it can run as soon as we call PTRACE_CONT below.
- // See details in client/debuggerd_client.cpp, in function
- // debugger_signal_handler().
-
- // Attach to the target process.
- if (!ptrace_attach_thread(request.pid, request.tid)) {
- ALOGE("debuggerd: ptrace attach failed: %s", strerror(errno));
- exit(1);
- }
-
- // DEBUGGER_ACTION_CRASH requests can come from arbitrary processes and the tid field in the
- // request is sent from the other side. If an attacker can cause a process to be spawned with the
- // pid of their process, they could trick debuggerd into dumping that process by exiting after
- // sending the request. Validate the trusted request.uid/gid to defend against this.
- if (request.action == DEBUGGER_ACTION_CRASH) {
- pid_t pid;
- uid_t uid;
- gid_t gid;
- if (get_process_info(request.tid, &pid, &uid, &gid) != 0) {
- ALOGE("debuggerd: failed to get process info for tid '%d'", request.tid);
- exit(1);
- }
-
- if (pid != request.pid || uid != request.uid || gid != request.gid) {
- ALOGE(
- "debuggerd: attached task %d does not match request: "
- "expected pid=%d,uid=%d,gid=%d, actual pid=%d,uid=%d,gid=%d",
- request.tid, request.pid, request.uid, request.gid, pid, uid, gid);
- exit(1);
- }
- }
-
- // Don't attach to the sibling threads if we want to attach gdb.
- // Supposedly, it makes the process less reliable.
- bool attach_gdb = should_attach_gdb(request);
- if (attach_gdb) {
- // Open all of the input devices we need to listen for VOLUMEDOWN before dropping privileges.
- if (init_getevent() != 0) {
- ALOGE("debuggerd: failed to initialize input device, not waiting for gdb");
- attach_gdb = false;
- }
-
- }
-
- std::set<pid_t> siblings;
- if (!attach_gdb) {
- ptrace_siblings(request.pid, request.tid, request.ignore_tid, siblings);
- }
-
- // Generate the backtrace map before dropping privileges.
- std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(request.pid));
-
- // Collect the list of open files before dropping privileges.
- OpenFilesList open_files;
- populate_open_files_list(request.pid, &open_files);
-
- int amfd = -1;
- std::unique_ptr<std::string> amfd_data;
- if (request.action == DEBUGGER_ACTION_CRASH) {
- // Connect to the activity manager before dropping privileges.
- amfd = activity_manager_connect();
- amfd_data.reset(new std::string);
- }
-
- bool succeeded = false;
-
- // Now that we've done everything that requires privileges, we can drop them.
- if (!drop_privileges()) {
- ALOGE("debuggerd: failed to drop privileges, exiting");
- _exit(1);
- }
-
- int crash_signal = SIGKILL;
- succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), open_files,
- siblings, &crash_signal, amfd_data.get());
- if (succeeded) {
- if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
- if (!tombstone_path.empty()) {
- android::base::WriteFully(fd, tombstone_path.c_str(), tombstone_path.length());
- }
- }
- }
-
- if (attach_gdb) {
- // Tell the signal process to send SIGSTOP to the target.
- if (!send_signal(request.pid, 0, SIGSTOP)) {
- ALOGE("debuggerd: failed to stop process for gdb attach: %s", strerror(errno));
- attach_gdb = false;
- }
- }
-
- if (!attach_gdb) {
- // Tell the Activity Manager about the crashing process. If we are
- // waiting for gdb to attach, do not send this or Activity Manager
- // might kill the process before anyone can attach.
- activity_manager_write(request.pid, crash_signal, amfd, *amfd_data.get());
- }
-
- if (ptrace(PTRACE_DETACH, request.tid, 0, 0) != 0) {
- ALOGE("debuggerd: ptrace detach from %d failed: %s", request.tid, strerror(errno));
- }
-
- for (pid_t sibling : siblings) {
- ptrace(PTRACE_DETACH, sibling, 0, 0);
- }
-
- // Send the signal back to the process if it crashed and we're not waiting for gdb.
- if (!attach_gdb && request.action == DEBUGGER_ACTION_CRASH) {
- if (!send_signal(request.pid, request.tid, crash_signal)) {
- ALOGE("debuggerd: failed to kill process %d: %s", request.pid, strerror(errno));
- }
- }
-
- // Wait for gdb, if requested.
- if (attach_gdb) {
- wait_for_user_action(request);
-
- // Now tell the activity manager about this process.
- activity_manager_write(request.pid, crash_signal, amfd, *amfd_data.get());
-
- // Tell the signal process to send SIGCONT to the target.
- if (!send_signal(request.pid, 0, SIGCONT)) {
- ALOGE("debuggerd: failed to resume process %d: %s", request.pid, strerror(errno));
- }
-
- uninit_getevent();
- }
-
- close(amfd);
-
- exit(!succeeded);
-}
-
-static void monitor_worker_process(int child_pid, const debugger_request_t& request) {
- struct timespec timeout = {.tv_sec = 10, .tv_nsec = 0 };
- if (should_attach_gdb(request)) {
- // If wait_for_gdb is enabled, set the timeout to something large.
- timeout.tv_sec = INT_MAX;
- }
-
- sigset_t signal_set;
- sigemptyset(&signal_set);
- sigaddset(&signal_set, SIGCHLD);
-
- bool kill_worker = false;
- bool kill_target = false;
- bool kill_self = false;
-
- int status;
- siginfo_t siginfo;
- int signal = TEMP_FAILURE_RETRY(sigtimedwait(&signal_set, &siginfo, &timeout));
- if (signal == SIGCHLD) {
- pid_t rc = waitpid(-1, &status, WNOHANG | WUNTRACED);
- if (rc != child_pid) {
- ALOGE("debuggerd: waitpid returned unexpected pid (%d), committing murder-suicide", rc);
-
- if (WIFEXITED(status)) {
- ALOGW("debuggerd: pid %d exited with status %d", rc, WEXITSTATUS(status));
- } else if (WIFSIGNALED(status)) {
- ALOGW("debuggerd: pid %d received signal %d", rc, WTERMSIG(status));
- } else if (WIFSTOPPED(status)) {
- ALOGW("debuggerd: pid %d stopped by signal %d", rc, WSTOPSIG(status));
- } else if (WIFCONTINUED(status)) {
- ALOGW("debuggerd: pid %d continued", rc);
- }
-
- kill_worker = true;
- kill_target = true;
- kill_self = true;
- } else if (WIFSIGNALED(status)) {
- ALOGE("debuggerd: worker process %d terminated due to signal %d", child_pid, WTERMSIG(status));
- kill_worker = false;
- kill_target = true;
- } else if (WIFSTOPPED(status)) {
- ALOGE("debuggerd: worker process %d stopped due to signal %d", child_pid, WSTOPSIG(status));
- kill_worker = true;
- kill_target = true;
- }
- } else {
- ALOGE("debuggerd: worker process %d timed out", child_pid);
- kill_worker = true;
- kill_target = true;
- }
-
- if (kill_worker) {
- // Something bad happened, kill the worker.
- if (kill(child_pid, SIGKILL) != 0) {
- ALOGE("debuggerd: failed to kill worker process %d: %s", child_pid, strerror(errno));
- } else {
- waitpid(child_pid, &status, 0);
- }
- }
-
- int exit_signal = SIGCONT;
- if (kill_target && request.action == DEBUGGER_ACTION_CRASH) {
- ALOGE("debuggerd: killing target %d", request.pid);
- exit_signal = SIGKILL;
- } else {
- ALOGW("debuggerd: resuming target %d", request.pid);
- }
-
- if (kill(request.pid, exit_signal) != 0) {
- ALOGE("debuggerd: failed to send signal %d to target: %s", exit_signal, strerror(errno));
- }
-
- if (kill_self) {
- stop_signal_sender();
- _exit(1);
- }
-}
-
-static void handle_request(int fd) {
- ALOGV("handle_request(%d)\n", fd);
-
- android::base::unique_fd closer(fd);
- debugger_request_t request;
- memset(&request, 0, sizeof(request));
- int status = read_request(fd, &request);
- if (status != 0) {
- return;
- }
-
- ALOGW("debuggerd: handling request: pid=%d uid=%d gid=%d tid=%d\n", request.pid, request.uid,
- request.gid, request.tid);
-
-#if defined(__LP64__)
- // On 64 bit systems, requests to dump 32 bit and 64 bit tids come
- // to the 64 bit debuggerd. If the process is a 32 bit executable,
- // redirect the request to the 32 bit debuggerd.
- if (is32bit(request.tid)) {
- // Only dump backtrace and dump tombstone requests can be redirected.
- if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE ||
- request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
- redirect_to_32(fd, &request);
- } else {
- ALOGE("debuggerd: Not allowed to redirect action %d to 32 bit debuggerd\n", request.action);
- }
- return;
- }
-#endif
-
- // Fork a child to handle the rest of the request.
- pid_t fork_pid = fork();
- if (fork_pid == -1) {
- ALOGE("debuggerd: failed to fork: %s\n", strerror(errno));
- } else if (fork_pid == 0) {
- worker_process(fd, request);
- } else {
- monitor_worker_process(fork_pid, request);
- }
-}
-
-static int do_server() {
- // debuggerd crashes can't be reported to debuggerd.
- // Reset all of the crash handlers.
- signal(SIGABRT, SIG_DFL);
- signal(SIGBUS, SIG_DFL);
- signal(SIGFPE, SIG_DFL);
- signal(SIGILL, SIG_DFL);
- signal(SIGSEGV, SIG_DFL);
-#ifdef SIGSTKFLT
- signal(SIGSTKFLT, SIG_DFL);
-#endif
- signal(SIGTRAP, SIG_DFL);
-
- // Ignore failed writes to closed sockets
- signal(SIGPIPE, SIG_IGN);
-
- // Block SIGCHLD so we can sigtimedwait for it.
- sigset_t sigchld;
- sigemptyset(&sigchld);
- sigaddset(&sigchld, SIGCHLD);
- sigprocmask(SIG_SETMASK, &sigchld, nullptr);
-
- int s = socket_local_server(SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
- SOCK_STREAM | SOCK_CLOEXEC);
- if (s == -1) return 1;
-
- // Fork a process that stays root, and listens on a pipe to pause and resume the target.
- if (!start_signal_sender()) {
- ALOGE("debuggerd: failed to fork signal sender");
- return 1;
- }
-
- ALOGI("debuggerd: starting\n");
-
- for (;;) {
- ALOGV("waiting for connection\n");
- int fd = accept4(s, nullptr, nullptr, SOCK_CLOEXEC | SOCK_NONBLOCK);
- if (fd == -1) {
- ALOGE("accept failed: %s\n", strerror(errno));
- continue;
- }
-
- handle_request(fd);
- }
- return 0;
-}
-
-static int do_explicit_dump(pid_t tid, bool dump_backtrace) {
- fprintf(stdout, "Sending request to dump task %d...\n", tid);
- fflush(stdout);
-
- // TODO: we could have better error reporting if debuggerd sent an error string back.
- if (dump_backtrace) {
- if (dump_backtrace_to_file(tid, fileno(stdout)) < 0) {
- fputs("Error dumping backtrace (check logcat).\n", stderr);
- return 1;
- }
- } else {
- char tombstone_path[PATH_MAX];
- if (dump_tombstone(tid, tombstone_path, sizeof(tombstone_path)) < 0) {
- fputs("Error dumping tombstone (check logcat).\n", stderr);
- return 1;
- }
- fprintf(stderr, "Tombstone written to: %s\n", tombstone_path);
- }
- return 0;
-}
-
-static int usage() {
- fputs("usage: debuggerd [-b] [<tid>]\n"
- "\n"
- "Given a thread id, sends a request to debuggerd to dump that thread.\n"
- "Otherwise, starts the debuggerd server.\n"
- "\n"
- "-b\tdump backtrace to console, otherwise generate tombstone\n", stderr);
- return EXIT_FAILURE;
-}
-
-int main(int argc, char** argv) {
- union selinux_callback cb;
- if (argc == 1) {
- cb.func_audit = audit_callback;
- selinux_set_callback(SELINUX_CB_AUDIT, cb);
- cb.func_log = selinux_log_callback;
- selinux_set_callback(SELINUX_CB_LOG, cb);
- return do_server();
- }
-
- bool dump_backtrace = false;
- pid_t tid = 0;
- for (int i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "-b")) {
- dump_backtrace = true;
- } else if (tid != 0 || (tid = atoi(argv[i])) == 0) {
- // Only one tid is allowed. (And 0 isn't a valid tid.)
- // atoi(3) returns 0 on failure to parse, so this catches anything else too.
- return usage();
- }
- }
- return (tid != 0) ? do_explicit_dump(tid, dump_backtrace) : usage();
-}
diff --git a/debuggerd/debuggerd.rc b/debuggerd/debuggerd.rc
deleted file mode 100644
index 1c6b9ff..0000000
--- a/debuggerd/debuggerd.rc
+++ /dev/null
@@ -1,3 +0,0 @@
-service debuggerd /system/bin/debuggerd
- group root readproc
- writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/debuggerd64.rc b/debuggerd/debuggerd64.rc
deleted file mode 100644
index 3e8847a..0000000
--- a/debuggerd/debuggerd64.rc
+++ /dev/null
@@ -1,3 +0,0 @@
-service debuggerd64 /system/bin/debuggerd64
- group root readproc
- writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
new file mode 100644
index 0000000..f51b5f2
--- /dev/null
+++ b/debuggerd/debuggerd_test.cpp
@@ -0,0 +1,390 @@
+/*
+ * Copyright 2016, 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 <err.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <chrono>
+#include <regex>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+#include <debuggerd/handler.h>
+#include <debuggerd/protocol.h>
+#include <debuggerd/util.h>
+#include <gtest/gtest.h>
+
+using namespace std::chrono_literals;
+using android::base::unique_fd;
+
+#if defined(__LP64__)
+#define CRASHER_PATH "/system/xbin/crasher64"
+#define ARCH_SUFFIX "64"
+#else
+#define CRASHER_PATH "/system/xbin/crasher"
+#define ARCH_SUFFIX ""
+#endif
+
+constexpr char kWaitForGdbKey[] = "debug.debuggerd.wait_for_gdb";
+
+#define TIMEOUT(seconds, expr) \
+ [&]() { \
+ struct sigaction old_sigaction; \
+ struct sigaction new_sigaction = {}; \
+ new_sigaction.sa_handler = [](int) {}; \
+ if (sigaction(SIGALRM, &new_sigaction, &new_sigaction) != 0) { \
+ err(1, "sigaction failed"); \
+ } \
+ alarm(seconds); \
+ auto value = expr; \
+ int saved_errno = errno; \
+ if (sigaction(SIGALRM, &old_sigaction, nullptr) != 0) { \
+ err(1, "sigaction failed"); \
+ } \
+ alarm(0); \
+ errno = saved_errno; \
+ return value; \
+ }()
+
+#define ASSERT_MATCH(str, pattern) \
+ do { \
+ std::regex r((pattern)); \
+ if (!std::regex_search((str), r)) { \
+ FAIL() << "regex mismatch: expected " << (pattern) << " in: \n" << (str); \
+ } \
+ } while (0)
+
+class CrasherTest : public ::testing::Test {
+ public:
+ pid_t crasher_pid = -1;
+ bool previous_wait_for_gdb;
+ unique_fd crasher_pipe;
+ unique_fd intercept_fd;
+
+ CrasherTest();
+ ~CrasherTest();
+
+ void StartIntercept(unique_fd* output_fd);
+
+ // Returns -1 if we fail to read a response from tombstoned, otherwise the received return code.
+ void FinishIntercept(int* result);
+
+ void StartCrasher(const std::string& crash_type);
+ void FinishCrasher();
+ void AssertDeath(int signo);
+};
+
+CrasherTest::CrasherTest() {
+ previous_wait_for_gdb = android::base::GetBoolProperty(kWaitForGdbKey, false);
+ android::base::SetProperty(kWaitForGdbKey, "0");
+}
+
+CrasherTest::~CrasherTest() {
+ if (crasher_pid != -1) {
+ kill(crasher_pid, SIGKILL);
+ int status;
+ waitpid(crasher_pid, &status, WUNTRACED);
+ }
+
+ android::base::SetProperty(kWaitForGdbKey, previous_wait_for_gdb ? "1" : "0");
+}
+
+void CrasherTest::StartIntercept(unique_fd* output_fd) {
+ if (crasher_pid == -1) {
+ FAIL() << "crasher hasn't been started";
+ }
+
+ intercept_fd.reset(socket_local_client(kTombstonedInterceptSocketName,
+ ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+ if (intercept_fd == -1) {
+ FAIL() << "failed to contact tombstoned: " << strerror(errno);
+ }
+
+ InterceptRequest req = {.pid = crasher_pid };
+
+ unique_fd output_pipe_write;
+ if (!Pipe(output_fd, &output_pipe_write)) {
+ FAIL() << "failed to create output pipe: " << strerror(errno);
+ }
+
+ std::string pipe_size_str;
+ int pipe_buffer_size;
+ if (!android::base::ReadFileToString("/proc/sys/fs/pipe-max-size", &pipe_size_str)) {
+ FAIL() << "failed to read /proc/sys/fs/pipe-max-size: " << strerror(errno);
+ }
+
+ pipe_size_str = android::base::Trim(pipe_size_str);
+
+ if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {
+ FAIL() << "failed to parse pipe max size";
+ }
+
+ if (fcntl(output_fd->get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
+ FAIL() << "failed to set pipe size: " << strerror(errno);
+ }
+
+ if (send_fd(intercept_fd.get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
+ FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
+ }
+}
+
+void CrasherTest::FinishIntercept(int* result) {
+ InterceptResponse response;
+
+ // Timeout for tombstoned intercept is 10 seconds.
+ ssize_t rc = TIMEOUT(20, read(intercept_fd.get(), &response, sizeof(response)));
+ if (rc == -1) {
+ FAIL() << "failed to read response from tombstoned: " << strerror(errno);
+ } else if (rc == 0) {
+ *result = -1;
+ } else if (rc != sizeof(response)) {
+ FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response)
+ << ", received " << rc;
+ } else {
+ *result = response.success;
+ }
+}
+
+void CrasherTest::StartCrasher(const std::string& crash_type) {
+ std::string type = "wait-" + crash_type;
+
+ unique_fd crasher_read_pipe;
+ if (!Pipe(&crasher_read_pipe, &crasher_pipe)) {
+ FAIL() << "failed to create pipe: " << strerror(errno);
+ }
+
+ crasher_pid = fork();
+ if (crasher_pid == -1) {
+ FAIL() << "fork failed: " << strerror(errno);
+ } else if (crasher_pid == 0) {
+ unique_fd devnull(open("/dev/null", O_WRONLY));
+ dup2(crasher_read_pipe.get(), STDIN_FILENO);
+ dup2(devnull.get(), STDOUT_FILENO);
+ dup2(devnull.get(), STDERR_FILENO);
+ execl(CRASHER_PATH, CRASHER_PATH, type.c_str(), nullptr);
+ err(1, "exec failed");
+ }
+}
+
+void CrasherTest::FinishCrasher() {
+ if (crasher_pipe == -1) {
+ FAIL() << "crasher pipe uninitialized";
+ }
+
+ ssize_t rc = write(crasher_pipe.get(), "\n", 1);
+ if (rc == -1) {
+ FAIL() << "failed to write to crasher pipe: " << strerror(errno);
+ } else if (rc == 0) {
+ FAIL() << "crasher pipe was closed";
+ }
+}
+
+void CrasherTest::AssertDeath(int signo) {
+ int status;
+ pid_t pid = TIMEOUT(5, waitpid(crasher_pid, &status, 0));
+ if (pid != crasher_pid) {
+ FAIL() << "failed to wait for crasher: " << strerror(errno);
+ }
+
+ if (!WIFSIGNALED(status)) {
+ FAIL() << "crasher didn't terminate via a signal";
+ }
+ ASSERT_EQ(signo, WTERMSIG(status));
+ crasher_pid = -1;
+}
+
+static void ConsumeFd(unique_fd fd, std::string* output) {
+ constexpr size_t read_length = PAGE_SIZE;
+ std::string result;
+
+ while (true) {
+ size_t offset = result.size();
+ result.resize(result.size() + PAGE_SIZE);
+ ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &result[offset], read_length));
+ if (rc == -1) {
+ FAIL() << "read failed: " << strerror(errno);
+ } else if (rc == 0) {
+ result.resize(result.size() - PAGE_SIZE);
+ break;
+ }
+
+ result.resize(result.size() - PAGE_SIZE + rc);
+ }
+
+ *output = std::move(result);
+}
+
+TEST_F(CrasherTest, smoke) {
+ int intercept_result;
+ unique_fd output_fd;
+ StartCrasher("SIGSEGV");
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)");
+}
+
+TEST_F(CrasherTest, abort) {
+ int intercept_result;
+ unique_fd output_fd;
+ StartCrasher("abort");
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGABRT);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+}
+
+TEST_F(CrasherTest, signal) {
+ int intercept_result;
+ unique_fd output_fd;
+ StartCrasher("abort");
+ StartIntercept(&output_fd);
+
+ // Wait for a bit, or we might end up killing the process before the signal
+ // handler even gets a chance to be registered.
+ std::this_thread::sleep_for(100ms);
+ ASSERT_EQ(0, kill(crasher_pid, SIGSEGV));
+
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 0 \(SI_USER\), fault addr --------)");
+ ASSERT_MATCH(result, R"(backtrace:)");
+}
+
+TEST_F(CrasherTest, abort_message) {
+ int intercept_result;
+ unique_fd output_fd;
+ StartCrasher("smash-stack");
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGABRT);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(Abort message: 'stack corruption detected \(-fstack-protector\)')");
+}
+
+TEST_F(CrasherTest, intercept_timeout) {
+ int intercept_result;
+ unique_fd output_fd;
+ StartCrasher("abort");
+ StartIntercept(&output_fd);
+
+ // Don't let crasher finish until we timeout.
+ FinishIntercept(&intercept_result);
+
+ ASSERT_NE(1, intercept_result) << "tombstoned reported success? (intercept_result = "
+ << intercept_result << ")";
+
+ FinishCrasher();
+ AssertDeath(SIGABRT);
+}
+
+TEST_F(CrasherTest, wait_for_gdb) {
+ if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
+ FAIL() << "failed to enable wait_for_gdb";
+ }
+ sleep(1);
+
+ StartCrasher("abort");
+ FinishCrasher();
+
+ int status;
+ ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, WUNTRACED));
+ ASSERT_TRUE(WIFSTOPPED(status));
+ ASSERT_EQ(SIGSTOP, WSTOPSIG(status));
+
+ ASSERT_EQ(0, kill(crasher_pid, SIGCONT));
+
+ AssertDeath(SIGABRT);
+}
+
+TEST_F(CrasherTest, wait_for_gdb_signal) {
+ if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
+ FAIL() << "failed to enable wait_for_gdb";
+ }
+
+ StartCrasher("abort");
+ ASSERT_EQ(0, kill(crasher_pid, SIGABRT)) << strerror(errno);
+
+ std::this_thread::sleep_for(500ms);
+
+ int status;
+ ASSERT_EQ(crasher_pid, (TIMEOUT(1, waitpid(crasher_pid, &status, WUNTRACED))));
+ ASSERT_TRUE(WIFSTOPPED(status));
+ ASSERT_EQ(SIGSTOP, WSTOPSIG(status));
+
+ ASSERT_EQ(0, kill(crasher_pid, SIGCONT)) << strerror(errno);
+
+ AssertDeath(SIGABRT);
+}
+
+TEST_F(CrasherTest, backtrace) {
+ std::string result;
+ int intercept_result;
+ unique_fd output_fd;
+ StartCrasher("abort");
+ StartIntercept(&output_fd);
+
+ std::this_thread::sleep_for(500ms);
+
+ sigval val;
+ val.sival_int = 1;
+ ASSERT_EQ(0, sigqueue(crasher_pid, DEBUGGER_SIGNAL, val)) << strerror(errno);
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(read\+)");
+
+ int status;
+ ASSERT_EQ(0, waitpid(crasher_pid, &status, WNOHANG | WUNTRACED));
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGABRT);
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+}
diff --git a/debuggerd/getevent.cpp b/debuggerd/getevent.cpp
deleted file mode 100644
index dbb878a..0000000
--- a/debuggerd/getevent.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * 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 <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/input.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/inotify.h>
-#include <sys/ioctl.h>
-#include <sys/limits.h>
-#include <sys/poll.h>
-#include <unistd.h>
-
-#include <memory>
-
-#include <android/log.h>
-
-static struct pollfd* ufds;
-static char** device_names;
-static int nfds;
-
-static int open_device(const char* device) {
- int version;
- int fd;
- struct pollfd* new_ufds;
- char** new_device_names;
- char name[80];
- char location[80];
- char idstr[80];
- struct input_id id;
-
- fd = open(device, O_RDWR);
- if (fd < 0) {
- return -1;
- }
-
- if (ioctl(fd, EVIOCGVERSION, &version)) {
- return -1;
- }
- if (ioctl(fd, EVIOCGID, &id)) {
- return -1;
- }
- name[sizeof(name) - 1] = '\0';
- location[sizeof(location) - 1] = '\0';
- idstr[sizeof(idstr) - 1] = '\0';
- if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
- name[0] = '\0';
- }
- if (ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
- location[0] = '\0';
- }
- if (ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {
- idstr[0] = '\0';
- }
-
- new_ufds = reinterpret_cast<pollfd*>(realloc(ufds, sizeof(ufds[0]) * (nfds + 1)));
- if (new_ufds == NULL) {
- fprintf(stderr, "out of memory\n");
- return -1;
- }
- ufds = new_ufds;
- new_device_names = reinterpret_cast<char**>(realloc(
- device_names, sizeof(device_names[0]) * (nfds + 1)));
- if (new_device_names == NULL) {
- fprintf(stderr, "out of memory\n");
- return -1;
- }
- device_names = new_device_names;
- ufds[nfds].fd = fd;
- ufds[nfds].events = POLLIN;
- device_names[nfds] = strdup(device);
- nfds++;
-
- return 0;
-}
-
-int close_device(const char* device) {
- int i;
- for (i = 1; i < nfds; i++) {
- if (strcmp(device_names[i], device) == 0) {
- int count = nfds - i - 1;
- free(device_names[i]);
- memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count);
- memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count);
- nfds--;
- return 0;
- }
- }
- return -1;
-}
-
-static int read_notify(const char* dirname, int nfd) {
- int res;
- char devname[PATH_MAX];
- char* filename;
- char event_buf[512];
- int event_size;
- int event_pos = 0;
- struct inotify_event *event;
-
- res = read(nfd, event_buf, sizeof(event_buf));
- if (res < (int)sizeof(*event)) {
- if (errno == EINTR)
- return 0;
- fprintf(stderr, "could not get event, %s\n", strerror(errno));
- return 1;
- }
-
- strcpy(devname, dirname);
- filename = devname + strlen(devname);
- *filename++ = '/';
-
- while (res >= (int)sizeof(*event)) {
- event = reinterpret_cast<struct inotify_event*>(event_buf + event_pos);
- if (event->len) {
- strcpy(filename, event->name);
- if (event->mask & IN_CREATE) {
- open_device(devname);
- } else {
- close_device(devname);
- }
- }
- event_size = sizeof(*event) + event->len;
- res -= event_size;
- event_pos += event_size;
- }
- return 0;
-}
-
-static int scan_dir(const char* dirname) {
- char devname[PATH_MAX];
- char* filename;
- struct dirent* de;
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir);
- if (dir == NULL)
- return -1;
- strcpy(devname, dirname);
- filename = devname + strlen(devname);
- *filename++ = '/';
- while ((de = readdir(dir.get()))) {
- if ((de->d_name[0] == '.' && de->d_name[1] == '\0') ||
- (de->d_name[1] == '.' && de->d_name[2] == '\0'))
- continue;
- strcpy(filename, de->d_name);
- open_device(devname);
- }
- return 0;
-}
-
-int init_getevent() {
- int res;
- const char* device_path = "/dev/input";
-
- nfds = 1;
- ufds = reinterpret_cast<pollfd*>(calloc(1, sizeof(ufds[0])));
- ufds[0].fd = inotify_init();
- ufds[0].events = POLLIN;
-
- res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);
- if (res < 0) {
- return 1;
- }
- res = scan_dir(device_path);
- if (res < 0) {
- return 1;
- }
- return 0;
-}
-
-void uninit_getevent() {
- int i;
- for (i = 0; i < nfds; i++) {
- close(ufds[i].fd);
- }
- free(ufds);
- ufds = 0;
- nfds = 0;
-}
-
-int get_event(struct input_event* event, int timeout) {
- int res;
- int i;
- int pollres;
- const char* device_path = "/dev/input";
- while (1) {
- pollres = poll(ufds, nfds, timeout);
- if (pollres == 0) {
- return 1;
- }
- if (ufds[0].revents & POLLIN) {
- read_notify(device_path, ufds[0].fd);
- }
- for (i = 1; i < nfds; i++) {
- if (ufds[i].revents) {
- if (ufds[i].revents & POLLIN) {
- res = read(ufds[i].fd, event, sizeof(*event));
- if (res < static_cast<int>(sizeof(event))) {
- fprintf(stderr, "could not get event\n");
- return -1;
- }
- return 0;
- }
- }
- }
- }
- return 0;
-}
diff --git a/debuggerd/getevent.h b/debuggerd/getevent.h
deleted file mode 100644
index 426139d..0000000
--- a/debuggerd/getevent.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _DEBUGGERD_GETEVENT_H
-#define _DEBUGGERD_GETEVENT_H
-
-int init_getevent();
-void uninit_getevent();
-int get_event(struct input_event* event, int timeout);
-
-#endif // _DEBUGGERD_GETEVENT_H
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
new file mode 100644
index 0000000..6033a6b
--- /dev/null
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -0,0 +1,377 @@
+/*
+ * 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.
+ *
+ * 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 "debuggerd/handler.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <sched.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "private/libc_logging.h"
+
+// see man(2) prctl, specifically the section about PR_GET_NAME
+#define MAX_TASK_NAME_LEN (16)
+
+#if defined(__LP64__)
+#define CRASH_DUMP_NAME "crash_dump64"
+#else
+#define CRASH_DUMP_NAME "crash_dump32"
+#endif
+
+#define CRASH_DUMP_PATH "/system/bin/" CRASH_DUMP_NAME
+
+static debuggerd_callbacks_t g_callbacks;
+
+// Don't use __libc_fatal because it exits via abort, which might put us back into a signal handler.
+#define fatal(...) \
+ do { \
+ __libc_format_log(ANDROID_LOG_FATAL, "libc", __VA_ARGS__); \
+ _exit(1); \
+ } while (0)
+
+/*
+ * Writes a summary of the signal to the log file. We do this so that, if
+ * for some reason we're not able to contact debuggerd, there is still some
+ * indication of the failure in the log.
+ *
+ * We could be here as a result of native heap corruption, or while a
+ * mutex is being held, so we don't want to use any libc functions that
+ * could allocate memory or hold a lock.
+ */
+static void log_signal_summary(int signum, const siginfo_t* info) {
+ const char* signal_name = "???";
+ bool has_address = false;
+ switch (signum) {
+ case SIGABRT:
+ signal_name = "SIGABRT";
+ break;
+ case SIGBUS:
+ signal_name = "SIGBUS";
+ has_address = true;
+ break;
+ case SIGFPE:
+ signal_name = "SIGFPE";
+ has_address = true;
+ break;
+ case SIGILL:
+ signal_name = "SIGILL";
+ has_address = true;
+ break;
+ case SIGSEGV:
+ signal_name = "SIGSEGV";
+ has_address = true;
+ break;
+#if defined(SIGSTKFLT)
+ case SIGSTKFLT:
+ signal_name = "SIGSTKFLT";
+ break;
+#endif
+ case SIGSYS:
+ signal_name = "SIGSYS";
+ break;
+ case SIGTRAP:
+ signal_name = "SIGTRAP";
+ break;
+ }
+
+ char thread_name[MAX_TASK_NAME_LEN + 1]; // one more for termination
+ if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
+ strcpy(thread_name, "<name unknown>");
+ } else {
+ // short names are null terminated by prctl, but the man page
+ // implies that 16 byte names are not.
+ thread_name[MAX_TASK_NAME_LEN] = 0;
+ }
+
+ // "info" will be null if the siginfo_t information was not available.
+ // Many signals don't have an address or a code.
+ char code_desc[32]; // ", code -6"
+ char addr_desc[32]; // ", fault addr 0x1234"
+ addr_desc[0] = code_desc[0] = 0;
+ if (info != nullptr) {
+ __libc_format_buffer(code_desc, sizeof(code_desc), ", code %d", info->si_code);
+ if (has_address) {
+ __libc_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
+ }
+ }
+ __libc_format_log(ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s)%s%s in tid %d (%s)", signum,
+ signal_name, code_desc, addr_desc, gettid(), thread_name);
+}
+
+/*
+ * Returns true if the handler for signal "signum" has SA_SIGINFO set.
+ */
+static bool have_siginfo(int signum) {
+ struct sigaction old_action;
+ if (sigaction(signum, nullptr, &old_action) < 0) {
+ __libc_format_log(ANDROID_LOG_WARN, "libc", "Failed testing for SA_SIGINFO: %s",
+ strerror(errno));
+ return false;
+ }
+ return (old_action.sa_flags & SA_SIGINFO) != 0;
+}
+
+struct debugger_thread_info {
+ pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+ bool crash_dump_started = false;
+ pid_t crashing_tid;
+ pid_t pseudothread_tid;
+ int signal_number;
+ siginfo_t* info;
+};
+
+// Logging and contacting debuggerd requires free file descriptors, which we might not have.
+// Work around this by spawning a "thread" that shares its parent's address space, but not its file
+// descriptor table, so that we can close random file descriptors without affecting the original
+// process. Note that this doesn't go through pthread_create, so TLS is shared with the spawning
+// process.
+static void* pseudothread_stack;
+
+static int debuggerd_dispatch_pseudothread(void* arg) {
+ debugger_thread_info* thread_info = static_cast<debugger_thread_info*>(arg);
+
+ for (int i = 0; i < 1024; ++i) {
+ close(i);
+ }
+
+ int devnull = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
+
+ // devnull will be 0.
+ TEMP_FAILURE_RETRY(dup2(devnull, STDOUT_FILENO));
+ TEMP_FAILURE_RETRY(dup2(devnull, STDERR_FILENO));
+
+ int pipefds[2];
+ if (pipe(pipefds) != 0) {
+ fatal("failed to create pipe");
+ }
+
+ // Don't use fork(2) to avoid calling pthread_atfork handlers.
+ int forkpid = clone(nullptr, nullptr, SIGCHLD, nullptr);
+ if (forkpid == -1) {
+ __libc_format_log(ANDROID_LOG_FATAL, "libc", "failed to fork in debuggerd signal handler: %s",
+ strerror(errno));
+ } else if (forkpid == 0) {
+ TEMP_FAILURE_RETRY(dup2(pipefds[1], STDOUT_FILENO));
+ close(pipefds[0]);
+ close(pipefds[1]);
+
+ char buf[10];
+ snprintf(buf, sizeof(buf), "%d", thread_info->crashing_tid);
+ execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, buf, nullptr);
+
+ fatal("exec failed: %s", strerror(errno));
+ } else {
+ close(pipefds[1]);
+ char buf[4];
+ ssize_t rc = TEMP_FAILURE_RETRY(read(pipefds[0], &buf, sizeof(buf)));
+ if (rc == -1) {
+ __libc_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s", strerror(errno));
+ } else if (rc == 0) {
+ __libc_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec");
+ } else if (rc != 1) {
+ __libc_format_log(ANDROID_LOG_FATAL, "libc",
+ "read of IPC pipe returned unexpected value: %zd", rc);
+ } else {
+ if (buf[0] != '\1') {
+ __libc_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
+ } else {
+ thread_info->crash_dump_started = true;
+ }
+ }
+ close(pipefds[0]);
+
+ // Don't leave a zombie child.
+ siginfo_t child_siginfo;
+ if (TEMP_FAILURE_RETRY(waitid(P_PID, forkpid, &child_siginfo, WEXITED)) != 0) {
+ __libc_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s",
+ strerror(errno));
+ thread_info->crash_dump_started = false;
+ }
+ }
+
+ pthread_mutex_unlock(&thread_info->mutex);
+ return 0;
+}
+
+// Handler that does crash dumping by forking and doing the processing in the child.
+// Do this by ptracing the relevant thread, and then execing debuggerd to do the actual dump.
+static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) {
+ // Mutex to prevent multiple crashing threads from trying to talk
+ // to debuggerd at the same time.
+ static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
+ int ret = pthread_mutex_lock(&crash_mutex);
+ if (ret != 0) {
+ __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
+ return;
+ }
+
+ // It's possible somebody cleared the SA_SIGINFO flag, which would mean
+ // our "info" arg holds an undefined value.
+ if (!have_siginfo(signal_number)) {
+ info = nullptr;
+ }
+
+ log_signal_summary(signal_number, info);
+ if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) {
+ // process has disabled core dumps and PTRACE_ATTACH, and does not want to be dumped.
+ // Honor that intention by not connecting to debuggerd and asking it to dump our internal state.
+ __libc_format_log(ANDROID_LOG_INFO, "libc",
+ "Suppressing debuggerd output because prctl(PR_GET_DUMPABLE)==0");
+
+ pthread_mutex_unlock(&crash_mutex);
+ return;
+ }
+
+ void* abort_message = nullptr;
+ if (g_callbacks.get_abort_message) {
+ abort_message = g_callbacks.get_abort_message();
+ }
+
+ debugger_thread_info thread_info = {
+ .crashing_tid = gettid(),
+ .signal_number = signal_number,
+ .info = info
+ };
+ pthread_mutex_lock(&thread_info.mutex);
+
+ // Essentially pthread_create without CLONE_FILES (see debuggerd_dispatch_pseudothread).
+ pid_t child_pid = clone(debuggerd_dispatch_pseudothread, pseudothread_stack,
+ CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_SETTID,
+ &thread_info, nullptr, nullptr, &thread_info.pseudothread_tid);
+ if (child_pid == -1) {
+ fatal("failed to spawn debuggerd dispatch thread: %s", strerror(errno));
+ }
+
+ // Wait for the child to finish and unlock the mutex.
+ // This relies on bionic behavior that isn't guaranteed by the standard.
+ pthread_mutex_lock(&thread_info.mutex);
+
+ // Signals can either be fatal or nonfatal.
+ // For fatal signals, crash_dump will PTRACE_CONT us with the signal we
+ // crashed with, so that processes using waitpid on us will see that we
+ // exited with the correct exit status (e.g. so that sh will report
+ // "Segmentation fault" instead of "Killed"). For this to work, we need
+ // to deregister our signal handler for that signal before continuing.
+ if (signal_number != DEBUGGER_SIGNAL) {
+ signal(signal_number, SIG_DFL);
+ }
+
+ // We need to return from the signal handler so that debuggerd can dump the
+ // thread that crashed, but returning here does not guarantee that the signal
+ // will be thrown again, even for SIGSEGV and friends, since the signal could
+ // have been sent manually. Resend the signal with rt_tgsigqueueinfo(2) to
+ // preserve the SA_SIGINFO contents.
+ struct siginfo si;
+ if (!info) {
+ memset(&si, 0, sizeof(si));
+ si.si_code = SI_USER;
+ si.si_pid = getpid();
+ si.si_uid = getuid();
+ info = &si;
+ } else if (info->si_code >= 0 || info->si_code == SI_TKILL) {
+ // rt_tgsigqueueinfo(2)'s documentation appears to be incorrect on kernels
+ // that contain commit 66dd34a (3.9+). The manpage claims to only allow
+ // negative si_code values that are not SI_TKILL, but 66dd34a changed the
+ // check to allow all si_code values in calls coming from inside the house.
+ }
+
+ // Populate si_value with the abort message address, if found.
+ if (abort_message) {
+ info->si_value.sival_ptr = abort_message;
+ }
+
+ // Only resend the signal if we know that either crash_dump has ptraced us or
+ // the signal was fatal.
+ if (thread_info.crash_dump_started || signal_number != DEBUGGER_SIGNAL) {
+ int rc = syscall(SYS_rt_tgsigqueueinfo, getpid(), gettid(), signal_number, info);
+ if (rc != 0) {
+ fatal("failed to resend signal during crash: %s", strerror(errno));
+ }
+ }
+
+ if (signal_number == DEBUGGER_SIGNAL) {
+ pthread_mutex_unlock(&crash_mutex);
+ }
+}
+
+void debuggerd_init(debuggerd_callbacks_t* callbacks) {
+ if (callbacks) {
+ g_callbacks = *callbacks;
+ }
+
+ void* thread_stack_allocation =
+ mmap(nullptr, PAGE_SIZE * 3, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (thread_stack_allocation == MAP_FAILED) {
+ fatal("failed to allocate debuggerd thread stack");
+ }
+
+ char* stack = static_cast<char*>(thread_stack_allocation) + PAGE_SIZE;
+ if (mprotect(stack, PAGE_SIZE, PROT_READ | PROT_WRITE) != 0) {
+ fatal("failed to mprotect debuggerd thread stack");
+ }
+
+ // Stack grows negatively, set it to the last byte in the page...
+ stack = (stack + PAGE_SIZE - 1);
+ // and align it.
+ stack -= 15;
+ pseudothread_stack = stack;
+
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ sigfillset(&action.sa_mask);
+ action.sa_sigaction = debuggerd_signal_handler;
+ action.sa_flags = SA_RESTART | SA_SIGINFO;
+
+ // Use the alternate signal stack if available so we can catch stack overflows.
+ action.sa_flags |= SA_ONSTACK;
+
+ sigaction(SIGABRT, &action, nullptr);
+ sigaction(SIGBUS, &action, nullptr);
+ sigaction(SIGFPE, &action, nullptr);
+ sigaction(SIGILL, &action, nullptr);
+ sigaction(SIGSEGV, &action, nullptr);
+#if defined(SIGSTKFLT)
+ sigaction(SIGSTKFLT, &action, nullptr);
+#endif
+ sigaction(SIGTRAP, &action, nullptr);
+ sigaction(DEBUGGER_SIGNAL, &action, nullptr);
+}
diff --git a/debuggerd/include/debuggerd/client.h b/debuggerd/include/debuggerd/client.h
index aeb723b..91f143b 100644
--- a/debuggerd/include/debuggerd/client.h
+++ b/debuggerd/include/debuggerd/client.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 The Android Open Source Project
+ * Copyright 2016, 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.
@@ -16,44 +16,21 @@
#pragma once
-#include <stdint.h>
+#include <stdbool.h>
#include <sys/cdefs.h>
-#include <sys/types.h>
+#include <unistd.h>
-// On 32-bit devices, DEBUGGER_SOCKET_NAME is a 32-bit debuggerd.
-// On 64-bit devices, DEBUGGER_SOCKET_NAME is a 64-bit debuggerd.
-#define DEBUGGER_SOCKET_NAME "android:debuggerd"
+#include <android-base/unique_fd.h>
-// Used only on 64-bit devices for debuggerd32.
-#define DEBUGGER32_SOCKET_NAME "android:debuggerd32"
+enum DebuggerdDumpType {
+ kDebuggerdBacktrace,
+ kDebuggerdTombstone,
+};
-__BEGIN_DECLS
+// Trigger a dump of specified process to output_fd.
+// output_fd is *not* consumed, timeouts <= 0 will wait forever.
+bool debuggerd_trigger_dump(pid_t pid, android::base::unique_fd output_fd,
+ enum DebuggerdDumpType dump_type, int timeout_ms);
-typedef enum {
- // dump a crash
- DEBUGGER_ACTION_CRASH,
- // dump a tombstone file
- DEBUGGER_ACTION_DUMP_TOMBSTONE,
- // dump a backtrace only back to the socket
- DEBUGGER_ACTION_DUMP_BACKTRACE,
-} debugger_action_t;
-
-// Make sure that all values have a fixed size so that this structure
-// is the same for 32 bit and 64 bit processes.
-typedef struct __attribute__((packed)) {
- int32_t action;
- pid_t tid;
- pid_t ignore_tid;
- uint64_t abort_msg_address;
-} debugger_msg_t;
-
-// These callbacks are called in a signal handler, and thus must be async signal safe.
-// If null, the callbacks will not be called.
-typedef struct {
- struct abort_msg_t* (*get_abort_message)();
- void (*post_dump)();
-} debuggerd_callbacks_t;
-
-void debuggerd_init(debuggerd_callbacks_t* callbacks);
-
-__END_DECLS
+int dump_backtrace_to_file(pid_t tid, int fd);
+int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs);
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
new file mode 100644
index 0000000..302f4c2
--- /dev/null
+++ b/debuggerd/include/debuggerd/handler.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <signal.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+// These callbacks are called in a signal handler, and thus must be async signal safe.
+// If null, the callbacks will not be called.
+typedef struct {
+ struct abort_msg_t* (*get_abort_message)();
+ void (*post_dump)();
+} debuggerd_callbacks_t;
+
+void debuggerd_init(debuggerd_callbacks_t* callbacks);
+
+// DEBUGGER_ACTION_DUMP_TOMBSTONE and DEBUGGER_ACTION_DUMP_BACKTRACE are both
+// triggered via DEBUGGER_SIGNAL. The debugger_action_t is sent via si_value
+// using sigqueue(2) or equivalent. If no si_value is specified (e.g. if the
+// signal is sent by kill(2)), the default behavior is to print the backtrace
+// to the log.
+#define DEBUGGER_SIGNAL (__SIGRTMIN + 3)
+
+__END_DECLS
diff --git a/debuggerd/include/debuggerd/protocol.h b/debuggerd/include/debuggerd/protocol.h
new file mode 100644
index 0000000..bb2ab0d
--- /dev/null
+++ b/debuggerd/include/debuggerd/protocol.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+// Sockets in the ANDROID_SOCKET_NAMESPACE_RESERVED namespace.
+// Both sockets are SOCK_SEQPACKET sockets, so no explicit length field is needed.
+constexpr char kTombstonedCrashSocketName[] = "tombstoned_crash";
+constexpr char kTombstonedInterceptSocketName[] = "tombstoned_intercept";
+
+enum class CrashPacketType : uint8_t {
+ // Initial request from crash_dump.
+ kDumpRequest = 0,
+
+ // Notification of a completed crash dump.
+ // Sent after a dump is completed and the process has been untraced, but
+ // before it has been resumed with SIGCONT.
+ kCompletedDump,
+
+ // Responses to kRequest.
+ // kPerformDump sends along an output fd via cmsg(3).
+ kPerformDump = 128,
+ kAbortDump,
+};
+
+struct DumpRequest {
+ int32_t pid;
+};
+
+// The full packet must always be written, regardless of whether the union is used.
+struct TombstonedCrashPacket {
+ CrashPacketType packet_type;
+ union {
+ DumpRequest dump_request;
+ } packet;
+};
+
+// Comes with a file descriptor via SCM_RIGHTS.
+// This packet should be sent before an actual dump happens.
+struct InterceptRequest {
+ int32_t pid;
+};
+
+// Sent either immediately upon failure, or when the intercept has been used.
+struct InterceptResponse {
+ uint8_t success; // 0 or 1
+ char error_message[127]; // always null-terminated
+};
diff --git a/debuggerd/include/debuggerd/util.h b/debuggerd/include/debuggerd/util.h
new file mode 100644
index 0000000..6051714
--- /dev/null
+++ b/debuggerd/include/debuggerd/util.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <android-base/unique_fd.h>
+
+// *** WARNING ***
+// tombstoned's sockets are SOCK_SEQPACKET sockets.
+// Short reads are treated as errors and short writes are assumed to not happen.
+
+// Sends a packet with an attached fd.
+ssize_t send_fd(int sockfd, const void* _Nonnull data, size_t len, android::base::unique_fd fd);
+
+// Receives a packet and optionally, its attached fd.
+// If out_fd is non-null, packets can optionally have an attached fd.
+// If out_fd is null, received packets must not have an attached fd.
+//
+// Errors:
+// EOVERFLOW: sockfd is SOCK_DGRAM or SOCK_SEQPACKET and buffer is too small.
+// The first len bytes of the packet are stored in data, but the
+// rest of the packet is dropped.
+// ERANGE: too many file descriptors were attached to the packet.
+// ENOMSG: not enough file descriptors were attached to the packet.
+//
+// plus any errors returned by the underlying recvmsg.
+ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len,
+ android::base::unique_fd* _Nullable out_fd);
+
+bool Pipe(android::base::unique_fd* read, android::base::unique_fd* write);
diff --git a/debuggerd/arm/machine.cpp b/debuggerd/libdebuggerd/arm/machine.cpp
similarity index 98%
rename from debuggerd/arm/machine.cpp
rename to debuggerd/libdebuggerd/arm/machine.cpp
index 292edcb..78c2306 100644
--- a/debuggerd/arm/machine.cpp
+++ b/debuggerd/libdebuggerd/arm/machine.cpp
@@ -22,8 +22,8 @@
#include <string.h>
#include <sys/ptrace.h>
-#include <android/log.h>
#include <backtrace/Backtrace.h>
+#include <log/log.h>
#include "machine.h"
#include "utility.h"
diff --git a/debuggerd/arm64/machine.cpp b/debuggerd/libdebuggerd/arm64/machine.cpp
similarity index 98%
rename from debuggerd/arm64/machine.cpp
rename to debuggerd/libdebuggerd/arm64/machine.cpp
index cd1bd52..e7bf79a 100644
--- a/debuggerd/arm64/machine.cpp
+++ b/debuggerd/libdebuggerd/arm64/machine.cpp
@@ -24,8 +24,8 @@
#include <sys/ptrace.h>
#include <sys/uio.h>
-#include <android/log.h>
#include <backtrace/Backtrace.h>
+#include <log/log.h>
#include "machine.h"
#include "utility.h"
diff --git a/debuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
similarity index 98%
rename from debuggerd/backtrace.cpp
rename to debuggerd/libdebuggerd/backtrace.cpp
index 06c1efe..0664442 100644
--- a/debuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -31,8 +31,8 @@
#include <memory>
#include <string>
-#include <android/log.h>
#include <backtrace/Backtrace.h>
+#include <log/log.h>
#include "backtrace.h"
diff --git a/debuggerd/elf_utils.cpp b/debuggerd/libdebuggerd/elf_utils.cpp
similarity index 99%
rename from debuggerd/elf_utils.cpp
rename to debuggerd/libdebuggerd/elf_utils.cpp
index d760a37..4e798e2 100644
--- a/debuggerd/elf_utils.cpp
+++ b/debuggerd/libdebuggerd/elf_utils.cpp
@@ -23,9 +23,9 @@
#include <string>
-#include <android/log.h>
#include <android-base/stringprintf.h>
#include <backtrace/Backtrace.h>
+#include <log/log.h>
#include "elf_utils.h"
diff --git a/debuggerd/backtrace.h b/debuggerd/libdebuggerd/include/backtrace.h
similarity index 100%
rename from debuggerd/backtrace.h
rename to debuggerd/libdebuggerd/include/backtrace.h
diff --git a/debuggerd/elf_utils.h b/debuggerd/libdebuggerd/include/elf_utils.h
similarity index 100%
rename from debuggerd/elf_utils.h
rename to debuggerd/libdebuggerd/include/elf_utils.h
diff --git a/debuggerd/machine.h b/debuggerd/libdebuggerd/include/machine.h
similarity index 100%
rename from debuggerd/machine.h
rename to debuggerd/libdebuggerd/include/machine.h
diff --git a/debuggerd/open_files_list.h b/debuggerd/libdebuggerd/include/open_files_list.h
similarity index 100%
rename from debuggerd/open_files_list.h
rename to debuggerd/libdebuggerd/include/open_files_list.h
diff --git a/debuggerd/tombstone.h b/debuggerd/libdebuggerd/include/tombstone.h
similarity index 97%
rename from debuggerd/tombstone.h
rename to debuggerd/libdebuggerd/include/tombstone.h
index 126f804..4ff24af 100644
--- a/debuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/tombstone.h
@@ -23,6 +23,8 @@
#include <set>
#include <string>
+#include "open_files_list.h"
+
class BacktraceMap;
/* Create and open a tombstone file for writing.
diff --git a/debuggerd/utility.h b/debuggerd/libdebuggerd/include/utility.h
similarity index 96%
rename from debuggerd/utility.h
rename to debuggerd/libdebuggerd/include/utility.h
index f7a3f73..bbc4546 100644
--- a/debuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/utility.h
@@ -18,6 +18,7 @@
#ifndef _DEBUGGERD_UTILITY_H
#define _DEBUGGERD_UTILITY_H
+#include <signal.h>
#include <stdbool.h>
#include <sys/types.h>
@@ -78,7 +79,7 @@
void _LOG(log_t* log, logtype ltype, const char *fmt, ...)
__attribute__ ((format(printf, 3, 4)));
-int wait_for_signal(pid_t tid);
+bool wait_for_signal(pid_t tid, siginfo_t* siginfo);
void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...);
diff --git a/debuggerd/mips/machine.cpp b/debuggerd/libdebuggerd/mips/machine.cpp
similarity index 99%
rename from debuggerd/mips/machine.cpp
rename to debuggerd/libdebuggerd/mips/machine.cpp
index 99a9d65..cbf272a 100644
--- a/debuggerd/mips/machine.cpp
+++ b/debuggerd/libdebuggerd/mips/machine.cpp
@@ -22,8 +22,8 @@
#include <string.h>
#include <sys/ptrace.h>
-#include <android/log.h>
#include <backtrace/Backtrace.h>
+#include <log/log.h>
#include "machine.h"
#include "utility.h"
diff --git a/debuggerd/mips64/machine.cpp b/debuggerd/libdebuggerd/mips64/machine.cpp
similarity index 99%
rename from debuggerd/mips64/machine.cpp
rename to debuggerd/libdebuggerd/mips64/machine.cpp
index ecd1ca2..0a8d532 100644
--- a/debuggerd/mips64/machine.cpp
+++ b/debuggerd/libdebuggerd/mips64/machine.cpp
@@ -22,8 +22,8 @@
#include <string.h>
#include <sys/ptrace.h>
-#include <android/log.h>
#include <backtrace/Backtrace.h>
+#include <log/log.h>
#include "machine.h"
#include "utility.h"
diff --git a/debuggerd/open_files_list.cpp b/debuggerd/libdebuggerd/open_files_list.cpp
similarity index 98%
rename from debuggerd/open_files_list.cpp
rename to debuggerd/libdebuggerd/open_files_list.cpp
index 5ef2abc..5c7ea70 100644
--- a/debuggerd/open_files_list.cpp
+++ b/debuggerd/libdebuggerd/open_files_list.cpp
@@ -29,7 +29,7 @@
#include <vector>
#include <android-base/file.h>
-#include <android/log.h>
+#include <log/log.h>
#include "open_files_list.h"
diff --git a/debuggerd/test/BacktraceMock.h b/debuggerd/libdebuggerd/test/BacktraceMock.h
similarity index 100%
rename from debuggerd/test/BacktraceMock.h
rename to debuggerd/libdebuggerd/test/BacktraceMock.h
diff --git a/debuggerd/test/dump_memory_test.cpp b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
similarity index 100%
rename from debuggerd/test/dump_memory_test.cpp
rename to debuggerd/libdebuggerd/test/dump_memory_test.cpp
diff --git a/debuggerd/test/elf_fake.cpp b/debuggerd/libdebuggerd/test/elf_fake.cpp
similarity index 100%
rename from debuggerd/test/elf_fake.cpp
rename to debuggerd/libdebuggerd/test/elf_fake.cpp
diff --git a/debuggerd/test/elf_fake.h b/debuggerd/libdebuggerd/test/elf_fake.h
similarity index 100%
rename from debuggerd/test/elf_fake.h
rename to debuggerd/libdebuggerd/test/elf_fake.h
diff --git a/debuggerd/test/host_signal_fixup.h b/debuggerd/libdebuggerd/test/host_signal_fixup.h
similarity index 100%
rename from debuggerd/test/host_signal_fixup.h
rename to debuggerd/libdebuggerd/test/host_signal_fixup.h
diff --git a/debuggerd/test/log_fake.cpp b/debuggerd/libdebuggerd/test/log_fake.cpp
similarity index 100%
rename from debuggerd/test/log_fake.cpp
rename to debuggerd/libdebuggerd/test/log_fake.cpp
diff --git a/debuggerd/test/log_fake.h b/debuggerd/libdebuggerd/test/log_fake.h
similarity index 100%
rename from debuggerd/test/log_fake.h
rename to debuggerd/libdebuggerd/test/log_fake.h
diff --git a/debuggerd/test/open_files_list_test.cpp b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
similarity index 100%
rename from debuggerd/test/open_files_list_test.cpp
rename to debuggerd/libdebuggerd/test/open_files_list_test.cpp
diff --git a/debuggerd/test/property_fake.cpp b/debuggerd/libdebuggerd/test/property_fake.cpp
similarity index 100%
rename from debuggerd/test/property_fake.cpp
rename to debuggerd/libdebuggerd/test/property_fake.cpp
diff --git a/debuggerd/test/ptrace_fake.cpp b/debuggerd/libdebuggerd/test/ptrace_fake.cpp
similarity index 100%
rename from debuggerd/test/ptrace_fake.cpp
rename to debuggerd/libdebuggerd/test/ptrace_fake.cpp
diff --git a/debuggerd/test/ptrace_fake.h b/debuggerd/libdebuggerd/test/ptrace_fake.h
similarity index 100%
rename from debuggerd/test/ptrace_fake.h
rename to debuggerd/libdebuggerd/test/ptrace_fake.h
diff --git a/debuggerd/test/sys/system_properties.h b/debuggerd/libdebuggerd/test/sys/system_properties.h
similarity index 100%
rename from debuggerd/test/sys/system_properties.h
rename to debuggerd/libdebuggerd/test/sys/system_properties.h
diff --git a/debuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
similarity index 100%
rename from debuggerd/test/tombstone_test.cpp
rename to debuggerd/libdebuggerd/test/tombstone_test.cpp
diff --git a/debuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
similarity index 98%
rename from debuggerd/tombstone.cpp
rename to debuggerd/libdebuggerd/tombstone.cpp
index e76edb9..01e9cf6 100644
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -32,6 +32,7 @@
#include <memory>
#include <string>
+#include <android/log.h>
#include <android-base/stringprintf.h>
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
@@ -39,9 +40,8 @@
#include <log/log.h>
#include <log/logprint.h>
#include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
-#include <selinux/android.h>
+#include "debuggerd/handler.h"
#include "backtrace.h"
#include "elf_utils.h"
@@ -86,6 +86,7 @@
case SIGSTOP: return "SIGSTOP";
case SIGSYS: return "SIGSYS";
case SIGTRAP: return "SIGTRAP";
+ case DEBUGGER_SIGNAL: return "<debuggerd signal>";
default: return "?";
}
}
@@ -625,7 +626,9 @@
const OpenFilesList& open_files, pid_t pid, pid_t tid,
const std::set<pid_t>& siblings, uintptr_t abort_msg_address) {
// don't copy log messages to tombstone unless this is a dev device
- bool want_logs = __android_log_is_debuggable();
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.debuggable", value, "0");
+ bool want_logs = (value[0] == '1');
_LOG(log, logtype::HEADER,
"*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
diff --git a/debuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
similarity index 95%
rename from debuggerd/utility.cpp
rename to debuggerd/libdebuggerd/utility.cpp
index 419d36c..57209aa 100644
--- a/debuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -71,21 +71,25 @@
}
}
-int wait_for_signal(pid_t tid) {
+bool wait_for_signal(pid_t tid, siginfo_t* siginfo) {
while (true) {
int status;
pid_t n = TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL));
if (n == -1) {
ALOGE("waitpid failed: tid %d, %s", tid, strerror(errno));
- return -1;
+ return false;
} else if (n == tid) {
if (WIFSTOPPED(status)) {
- return WSTOPSIG(status);
+ if (ptrace(PTRACE_GETSIGINFO, tid, nullptr, siginfo) != 0) {
+ ALOGE("PTRACE_GETSIGINFO failed: %s", strerror(errno));
+ return false;
+ }
+ return true;
} else {
ALOGE("unexpected waitpid response: n=%d, status=%08x\n", n, status);
// This is the only circumstance under which we can allow a detach
// to fail with ESRCH, which indicates the tid has exited.
- return -1;
+ return false;
}
}
}
diff --git a/debuggerd/x86/machine.cpp b/debuggerd/libdebuggerd/x86/machine.cpp
similarity index 98%
rename from debuggerd/x86/machine.cpp
rename to debuggerd/libdebuggerd/x86/machine.cpp
index a6f21e1..af10817 100644
--- a/debuggerd/x86/machine.cpp
+++ b/debuggerd/libdebuggerd/x86/machine.cpp
@@ -21,8 +21,8 @@
#include <string.h>
#include <sys/ptrace.h>
-#include <android/log.h>
#include <backtrace/Backtrace.h>
+#include <log/log.h>
#include "machine.h"
#include "utility.h"
diff --git a/debuggerd/x86_64/machine.cpp b/debuggerd/libdebuggerd/x86_64/machine.cpp
similarity index 98%
rename from debuggerd/x86_64/machine.cpp
rename to debuggerd/libdebuggerd/x86_64/machine.cpp
index 705e12d..bf2c2b4 100644
--- a/debuggerd/x86_64/machine.cpp
+++ b/debuggerd/libdebuggerd/x86_64/machine.cpp
@@ -18,12 +18,12 @@
#include <errno.h>
#include <stdint.h>
-#include <sys/ptrace.h>
#include <string.h>
+#include <sys/ptrace.h>
#include <sys/user.h>
-#include <android/log.h>
#include <backtrace/Backtrace.h>
+#include <log/log.h>
#include "machine.h"
#include "utility.h"
diff --git a/debuggerd/signal_sender.cpp b/debuggerd/signal_sender.cpp
index 7fe4dee..42a8e77 100644
--- a/debuggerd/signal_sender.cpp
+++ b/debuggerd/signal_sender.cpp
@@ -27,7 +27,7 @@
#include <sys/wait.h>
#include <unistd.h>
-#include <android/log.h>
+#include <log/log.h>
#include "signal_sender.h"
diff --git a/debuggerd/signal_sender.h b/debuggerd/signal_sender.h
deleted file mode 100644
index 0443272..0000000
--- a/debuggerd/signal_sender.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2016 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 _DEBUGGERD_SIGNAL_SENDER_H
-#define _DEBUGGERD_SIGNAL_SENDER_H
-
-#include <sys/types.h>
-
-bool start_signal_sender();
-bool stop_signal_sender();
-
-// Sends a signal to a target process or thread.
-// If tid is greater than zero, this performs tgkill(pid, tid, signal).
-// Otherwise, it performs kill(pid, signal).
-bool send_signal(pid_t pid, pid_t tid, int signal);
-
-#endif
diff --git a/debuggerd/test/selinux/android.h b/debuggerd/test/selinux/android.h
deleted file mode 100644
index abed087..0000000
--- a/debuggerd/test/selinux/android.h
+++ /dev/null
@@ -1,17 +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.
- */
-
-extern "C" int selinux_android_restorecon(const char*, unsigned int);
diff --git a/debuggerd/test/selinux_fake.cpp b/debuggerd/test/selinux_fake.cpp
deleted file mode 100644
index acdd0a9..0000000
--- a/debuggerd/test/selinux_fake.cpp
+++ /dev/null
@@ -1,19 +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.
- */
-
-extern "C" int selinux_android_restorecon(const char*, unsigned int) {
- return 0;
-}
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
new file mode 100644
index 0000000..789260d
--- /dev/null
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2016, 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 "intercept_manager.h"
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include <unordered_map>
+
+#include <event2/event.h>
+#include <event2/listener.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+
+#include "debuggerd/protocol.h"
+#include "debuggerd/util.h"
+
+using android::base::unique_fd;
+
+static void intercept_close_cb(evutil_socket_t sockfd, short event, void* arg) {
+ auto intercept = reinterpret_cast<Intercept*>(arg);
+ InterceptManager* intercept_manager = intercept->intercept_manager;
+
+ CHECK_EQ(sockfd, intercept->sockfd.get());
+
+ // If we can read, either we received unexpected data from the other side, or the other side
+ // closed their end of the socket. Either way, kill the intercept.
+
+ // Ownership of intercept differs based on whether we've registered it with InterceptManager.
+ if (!intercept->registered) {
+ delete intercept;
+ } else {
+ auto it = intercept_manager->intercepts.find(intercept->intercept_pid);
+ if (it == intercept_manager->intercepts.end()) {
+ LOG(FATAL) << "intercept close callback called after intercept was already removed?";
+ }
+ if (it->second.get() != intercept) {
+ LOG(FATAL) << "intercept close callback has different Intercept from InterceptManager?";
+ }
+
+ const char* reason;
+ if ((event & EV_TIMEOUT) != 0) {
+ reason = "due to timeout";
+ } else {
+ reason = "due to input";
+ }
+
+ LOG(INFO) << "intercept for pid " << intercept->intercept_pid << " terminated " << reason;
+ intercept_manager->intercepts.erase(it);
+ }
+}
+
+static void intercept_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
+ auto intercept = reinterpret_cast<Intercept*>(arg);
+ InterceptManager* intercept_manager = intercept->intercept_manager;
+
+ CHECK_EQ(sockfd, intercept->sockfd.get());
+
+ if ((ev & EV_TIMEOUT) != 0) {
+ LOG(WARNING) << "tombstoned didn't receive InterceptRequest before timeout";
+ goto fail;
+ } else if ((ev & EV_READ) == 0) {
+ LOG(WARNING) << "tombstoned received unexpected event on intercept socket";
+ goto fail;
+ }
+
+ {
+ unique_fd rcv_fd;
+ InterceptRequest intercept_request;
+ ssize_t result = recv_fd(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
+
+ if (result == -1) {
+ PLOG(WARNING) << "failed to read from intercept socket";
+ goto fail;
+ } else if (result != sizeof(intercept_request)) {
+ LOG(WARNING) << "intercept socket received short read of length " << result << " (expected "
+ << sizeof(intercept_request) << ")";
+ goto fail;
+ }
+
+ // Move the received FD to the upper half, in order to more easily notice FD leaks.
+ int moved_fd = fcntl(rcv_fd.get(), F_DUPFD, 512);
+ if (moved_fd == -1) {
+ LOG(WARNING) << "failed to move received fd (" << rcv_fd.get() << ")";
+ goto fail;
+ }
+ rcv_fd.reset(moved_fd);
+
+ // We trust the other side, so only do minimal validity checking.
+ if (intercept_request.pid <= 0 || intercept_request.pid > std::numeric_limits<pid_t>::max()) {
+ InterceptResponse response = {};
+ snprintf(response.error_message, sizeof(response.error_message), "invalid pid %" PRId32,
+ intercept_request.pid);
+ TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
+ goto fail;
+ }
+
+ intercept->intercept_pid = intercept_request.pid;
+
+ // Register the intercept with the InterceptManager.
+ if (intercept_manager->intercepts.count(intercept_request.pid) > 0) {
+ InterceptResponse response = {};
+ snprintf(response.error_message, sizeof(response.error_message),
+ "pid %" PRId32 " already intercepted", intercept_request.pid);
+ TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
+ LOG(WARNING) << response.error_message;
+ goto fail;
+ }
+
+ intercept->output_fd = std::move(rcv_fd);
+ intercept_manager->intercepts[intercept_request.pid] = std::unique_ptr<Intercept>(intercept);
+ intercept->registered = true;
+
+ LOG(INFO) << "tombstoned registered intercept for pid " << intercept_request.pid;
+
+ // Register a different read event on the socket so that we can remove intercepts if the socket
+ // closes (e.g. if a user CTRL-C's the process that requested the intercept).
+ event_assign(intercept->intercept_event, intercept_manager->base, sockfd, EV_READ | EV_TIMEOUT,
+ intercept_close_cb, arg);
+
+ struct timeval timeout = { .tv_sec = 10, .tv_usec = 0 };
+ event_add(intercept->intercept_event, &timeout);
+ }
+
+ return;
+
+fail:
+ delete intercept;
+}
+
+static void intercept_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
+ void* arg) {
+ Intercept* intercept = new Intercept();
+ intercept->intercept_manager = static_cast<InterceptManager*>(arg);
+ intercept->sockfd.reset(sockfd);
+
+ struct timeval timeout = { 1, 0 };
+ event_base* base = evconnlistener_get_base(listener);
+ event* intercept_event =
+ event_new(base, sockfd, EV_TIMEOUT | EV_READ, intercept_request_cb, intercept);
+ intercept->intercept_event = intercept_event;
+ event_add(intercept_event, &timeout);
+}
+
+InterceptManager::InterceptManager(event_base* base, int intercept_socket) : base(base) {
+ this->listener = evconnlistener_new(base, intercept_accept_cb, this, -1, LEV_OPT_CLOSE_ON_FREE,
+ intercept_socket);
+}
+
+bool InterceptManager::GetIntercept(pid_t pid, android::base::unique_fd* out_fd) {
+ auto it = this->intercepts.find(pid);
+ if (it == this->intercepts.end()) {
+ return false;
+ }
+
+ auto intercept = std::move(it->second);
+ this->intercepts.erase(it);
+
+ LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid;
+ InterceptResponse response = {};
+ response.success = 1;
+ TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response)));
+ *out_fd = std::move(intercept->output_fd);
+
+ return true;
+}
diff --git a/debuggerd/tombstoned/intercept_manager.h b/debuggerd/tombstoned/intercept_manager.h
new file mode 100644
index 0000000..cb5db62
--- /dev/null
+++ b/debuggerd/tombstoned/intercept_manager.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <unordered_map>
+
+#include <event2/event.h>
+#include <event2/listener.h>
+
+#include <android-base/unique_fd.h>
+
+struct InterceptManager;
+
+struct Intercept {
+ ~Intercept() {
+ event_free(intercept_event);
+ }
+
+ InterceptManager* intercept_manager = nullptr;
+ event* intercept_event = nullptr;
+ android::base::unique_fd sockfd;
+
+ pid_t intercept_pid = -1;
+ android::base::unique_fd output_fd;
+ bool registered = false;
+};
+
+struct InterceptManager {
+ event_base* base;
+ std::unordered_map<pid_t, std::unique_ptr<Intercept>> intercepts;
+ evconnlistener* listener = nullptr;
+
+ InterceptManager(event_base* _Nonnull base, int intercept_socket);
+ InterceptManager(InterceptManager& copy) = delete;
+ InterceptManager(InterceptManager&& move) = delete;
+
+ bool GetIntercept(pid_t pid, android::base::unique_fd* out_fd);
+};
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
new file mode 100644
index 0000000..3c1dcaf
--- /dev/null
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2016, 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 <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <array>
+#include <deque>
+#include <unordered_map>
+
+#include <event2/event.h>
+#include <event2/listener.h>
+#include <event2/thread.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+
+#include "debuggerd/protocol.h"
+#include "debuggerd/util.h"
+
+#include "intercept_manager.h"
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+static InterceptManager* intercept_manager;
+
+enum CrashStatus {
+ kCrashStatusRunning,
+ kCrashStatusQueued,
+};
+
+// Ownership of Crash is a bit messy.
+// It's either owned by an active event that must have a timeout, or owned by
+// queued_requests, in the case that multiple crashes come in at the same time.
+struct Crash {
+ ~Crash() {
+ event_free(crash_event);
+ }
+
+ unique_fd crash_fd;
+ pid_t crash_pid;
+ event* crash_event = nullptr;
+};
+
+static constexpr char kTombstoneDirectory[] = "/data/tombstones/";
+static constexpr size_t kTombstoneCount = 10;
+static int tombstone_directory_fd = -1;
+static int next_tombstone = 0;
+
+static constexpr size_t kMaxConcurrentDumps = 1;
+static size_t num_concurrent_dumps = 0;
+
+static std::deque<Crash*> queued_requests;
+
+// Forward declare the callbacks so they can be placed in a sensible order.
+static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int, void*);
+static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg);
+static void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg);
+
+static void find_oldest_tombstone() {
+ size_t oldest_tombstone = 0;
+ time_t oldest_time = std::numeric_limits<time_t>::max();
+
+ for (size_t i = 0; i < kTombstoneCount; ++i) {
+ std::string path = android::base::StringPrintf("%stombstone_%02zu", kTombstoneDirectory, i);
+ struct stat st;
+ if (stat(path.c_str(), &st) != 0) {
+ PLOG(ERROR) << "failed to stat " << path;
+ }
+
+ if (st.st_mtime < oldest_time) {
+ oldest_tombstone = i;
+ oldest_time = st.st_mtime;
+ }
+ }
+
+ next_tombstone = oldest_tombstone;
+}
+
+static unique_fd get_tombstone_fd() {
+ // If kMaxConcurrentDumps is greater than 1, then theoretically the same
+ // filename could be handed out to multiple processes. Unlink and create the
+ // file, instead of using O_TRUNC, to avoid two processes interleaving their
+ // output.
+ unique_fd result;
+ char buf[PATH_MAX];
+ snprintf(buf, sizeof(buf), "tombstone_%02d", next_tombstone);
+ if (unlinkat(tombstone_directory_fd, buf, 0) != 0 && errno != ENOENT) {
+ PLOG(FATAL) << "failed to unlink tombstone at " << kTombstoneDirectory << buf;
+ }
+
+ result.reset(
+ openat(tombstone_directory_fd, buf, O_CREAT | O_EXCL | O_WRONLY | O_APPEND, O_CLOEXEC, 0700));
+ if (result == -1) {
+ PLOG(FATAL) << "failed to create tombstone at " << kTombstoneDirectory << buf;
+ }
+
+ next_tombstone = (next_tombstone + 1) % kTombstoneCount;
+ return result;
+}
+
+static void dequeue_request(Crash* crash) {
+ ++num_concurrent_dumps;
+
+ unique_fd output_fd;
+ if (!intercept_manager->GetIntercept(crash->crash_pid, &output_fd)) {
+ output_fd = get_tombstone_fd();
+ }
+
+ TombstonedCrashPacket response = {
+ .packet_type = CrashPacketType::kPerformDump
+ };
+ ssize_t rc = send_fd(crash->crash_fd, &response, sizeof(response), std::move(output_fd));
+ if (rc == -1) {
+ PLOG(WARNING) << "failed to send response to CrashRequest";
+ goto fail;
+ } else if (rc != sizeof(response)) {
+ PLOG(WARNING) << "crash socket write returned short";
+ goto fail;
+ } else {
+ // TODO: Make this configurable by the interceptor?
+ struct timeval timeout = { 10, 0 };
+
+ event_base* base = event_get_base(crash->crash_event);
+ event_assign(crash->crash_event, base, crash->crash_fd, EV_TIMEOUT | EV_READ,
+ crash_completed_cb, crash);
+ event_add(crash->crash_event, &timeout);
+ }
+ return;
+
+fail:
+ delete crash;
+}
+
+static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
+ void*) {
+ event_base* base = evconnlistener_get_base(listener);
+ Crash* crash = new Crash();
+
+ struct timeval timeout = { 1, 0 };
+ event* crash_event = event_new(base, sockfd, EV_TIMEOUT | EV_READ, crash_request_cb, crash);
+ crash->crash_fd.reset(sockfd);
+ crash->crash_event = crash_event;
+ event_add(crash_event, &timeout);
+}
+
+static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
+ ssize_t rc;
+ Crash* crash = static_cast<Crash*>(arg);
+ TombstonedCrashPacket request = {};
+
+ if ((ev & EV_TIMEOUT) != 0) {
+ LOG(WARNING) << "crash request timed out";
+ goto fail;
+ } else if ((ev & EV_READ) == 0) {
+ LOG(WARNING) << "tombstoned received unexpected event from crash socket";
+ goto fail;
+ }
+
+ rc = TEMP_FAILURE_RETRY(read(sockfd, &request, sizeof(request)));
+ if (rc == -1) {
+ PLOG(WARNING) << "failed to read from crash socket";
+ goto fail;
+ } else if (rc != sizeof(request)) {
+ LOG(WARNING) << "crash socket received short read of length " << rc << " (expected "
+ << sizeof(request) << ")";
+ goto fail;
+ }
+
+ if (request.packet_type != CrashPacketType::kDumpRequest) {
+ LOG(WARNING) << "unexpected crash packet type, expected kDumpRequest, received "
+ << StringPrintf("%#2hhX", request.packet_type);
+ goto fail;
+ }
+
+ crash->crash_pid = request.packet.dump_request.pid;
+ LOG(INFO) << "received crash request for pid " << crash->crash_pid;
+
+ if (num_concurrent_dumps == kMaxConcurrentDumps) {
+ LOG(INFO) << "enqueueing crash request for pid " << crash->crash_pid;
+ queued_requests.push_back(crash);
+ } else {
+ dequeue_request(crash);
+ }
+
+ return;
+
+fail:
+ delete crash;
+}
+
+static void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg) {
+ ssize_t rc;
+ Crash* crash = static_cast<Crash*>(arg);
+ TombstonedCrashPacket request = {};
+
+ --num_concurrent_dumps;
+
+ if ((ev & EV_READ) == 0) {
+ goto fail;
+ }
+
+ rc = TEMP_FAILURE_RETRY(read(sockfd, &request, sizeof(request)));
+ if (rc == -1) {
+ PLOG(WARNING) << "failed to read from crash socket";
+ goto fail;
+ } else if (rc != sizeof(request)) {
+ LOG(WARNING) << "crash socket received short read of length " << rc << " (expected "
+ << sizeof(request) << ")";
+ goto fail;
+ }
+
+ if (request.packet_type != CrashPacketType::kCompletedDump) {
+ LOG(WARNING) << "unexpected crash packet type, expected kCompletedDump, received "
+ << uint32_t(request.packet_type);
+ goto fail;
+ }
+
+fail:
+ delete crash;
+
+ // If there's something queued up, let them proceed.
+ if (!queued_requests.empty()) {
+ Crash* next_crash = queued_requests.front();
+ queued_requests.pop_front();
+ dequeue_request(next_crash);
+ }
+}
+
+int main(int, char* []) {
+ tombstone_directory_fd = open(kTombstoneDirectory, O_DIRECTORY | O_RDONLY | O_CLOEXEC);
+ if (tombstone_directory_fd == -1) {
+ PLOG(FATAL) << "failed to open tombstone directory";
+ }
+
+ find_oldest_tombstone();
+
+ int intercept_socket = android_get_control_socket(kTombstonedInterceptSocketName);
+ int crash_socket = android_get_control_socket(kTombstonedCrashSocketName);
+
+ if (intercept_socket == -1 || crash_socket == -1) {
+ PLOG(FATAL) << "failed to get socket from init";
+ }
+
+ evutil_make_socket_nonblocking(intercept_socket);
+ evutil_make_socket_nonblocking(crash_socket);
+
+ event_base* base = event_base_new();
+ if (!base) {
+ LOG(FATAL) << "failed to create event_base";
+ }
+
+ intercept_manager = new InterceptManager(base, intercept_socket);
+
+ evconnlistener* listener =
+ evconnlistener_new(base, crash_accept_cb, nullptr, -1, LEV_OPT_CLOSE_ON_FREE, crash_socket);
+ if (!listener) {
+ LOG(FATAL) << "failed to create evconnlistener";
+ }
+
+ LOG(INFO) << "tombstoned successfully initialized";
+ event_base_dispatch(base);
+}
diff --git a/debuggerd/tombstoned/tombstoned.rc b/debuggerd/tombstoned/tombstoned.rc
new file mode 100644
index 0000000..3aacf33
--- /dev/null
+++ b/debuggerd/tombstoned/tombstoned.rc
@@ -0,0 +1,9 @@
+service tombstoned /system/bin/tombstoned
+ class core
+
+ user tombstoned
+ group system
+
+ socket tombstoned_crash seqpacket 0666 system system
+ socket tombstoned_intercept seqpacket 0666 system system
+ writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
new file mode 100644
index 0000000..738abdf
--- /dev/null
+++ b/debuggerd/util.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "debuggerd/util.h"
+
+#include <sys/socket.h>
+
+#include <utility>
+
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+
+ssize_t send_fd(int sockfd, const void* data, size_t len, android::base::unique_fd fd) {
+ char cmsg_buf[CMSG_SPACE(sizeof(int))];
+
+ iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
+ msghdr msg = {
+ .msg_iov = &iov, .msg_iovlen = 1, .msg_control = cmsg_buf, .msg_controllen = sizeof(cmsg_buf),
+ };
+ auto cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ *reinterpret_cast<int*>(CMSG_DATA(cmsg)) = fd.get();
+
+ return TEMP_FAILURE_RETRY(sendmsg(sockfd, &msg, 0));
+}
+
+ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len,
+ android::base::unique_fd* _Nullable out_fd) {
+ char cmsg_buf[CMSG_SPACE(sizeof(int))];
+
+ iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
+ msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = cmsg_buf,
+ .msg_controllen = sizeof(cmsg_buf),
+ .msg_flags = 0,
+ };
+ auto cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+
+ ssize_t result = TEMP_FAILURE_RETRY(recvmsg(sockfd, &msg, 0));
+ if (result == -1) {
+ return -1;
+ }
+
+ android::base::unique_fd fd;
+ bool received_fd = msg.msg_controllen == sizeof(cmsg_buf);
+ if (received_fd) {
+ fd.reset(*reinterpret_cast<int*>(CMSG_DATA(cmsg)));
+ }
+
+ if ((msg.msg_flags & MSG_TRUNC) != 0) {
+ errno = EFBIG;
+ return -1;
+ } else if ((msg.msg_flags & MSG_CTRUNC) != 0) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ if (out_fd) {
+ *out_fd = std::move(fd);
+ } else if (received_fd) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ return result;
+}
+
+bool Pipe(android::base::unique_fd* read, android::base::unique_fd* write) {
+ int pipefds[2];
+ if (pipe(pipefds) != 0) {
+ return false;
+ }
+ read->reset(pipefds[0]);
+ write->reset(pipefds[1]);
+ return true;
+}
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 286de5b..5610cc0 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -57,8 +57,8 @@
LOCAL_STATIC_LIBRARIES := \
libziparchive \
- libext4_utils_host \
- libsparse_host \
+ libext4_utils \
+ libsparse \
libutils \
liblog \
libz \
diff --git a/fastboot/README.md b/fastboot/README.md
new file mode 100644
index 0000000..022d34b
--- /dev/null
+++ b/fastboot/README.md
@@ -0,0 +1,453 @@
+Fastboot
+--------
+
+The fastboot protocol is a mechanism for communicating with bootloaders
+over USB or ethernet. It is designed to be very straightforward to implement,
+to allow it to be used across a wide range of devices and from hosts running
+Linux, macOS, or Windows.
+
+
+## Basic Requirements
+
+* USB
+ * Two bulk endpoints (in, out) are required
+ * Max packet size must be 64 bytes for full-speed, 512 bytes for
+ high-speed and 1024 bytes for Super Speed USB.
+ * The protocol is entirely host-driven and synchronous (unlike the
+ multi-channel, bi-directional, asynchronous ADB protocol)
+
+* TCP or UDP
+ * Device must be reachable via IP.
+ * Device will act as the server, fastboot will be the client.
+ * Fastboot data is wrapped in a simple protocol; see below for details.
+
+
+## Transport and Framing
+
+1. Host sends a command, which is an ascii string in a single
+ packet no greater than 64 bytes.
+
+2. Client response with a single packet no greater than 64 bytes.
+ The first four bytes of the response are "OKAY", "FAIL", "DATA",
+ or "INFO". Additional bytes may contain an (ascii) informative
+ message.
+
+ a. INFO -> the remaining 60 bytes are an informative message
+ (providing progress or diagnostic messages). They should
+ be displayed and then step #2 repeats
+
+ b. FAIL -> the requested command failed. The remaining 60 bytes
+ of the response (if present) provide a textual failure message
+ to present to the user. Stop.
+
+ c. OKAY -> the requested command completed successfully. Go to #5
+
+ d. DATA -> the requested command is ready for the data phase.
+ A DATA response packet will be 12 bytes long, in the form of
+ DATA00000000 where the 8 digit hexadecimal number represents
+ the total data size to transfer.
+
+3. Data phase. Depending on the command, the host or client will
+ send the indicated amount of data. Short packets are always
+ acceptable and zero-length packets are ignored. This phase continues
+ until the client has sent or received the number of bytes indicated
+ in the "DATA" response above.
+
+4. Client responds with a single packet no greater than 64 bytes.
+ The first four bytes of the response are "OKAY", "FAIL", or "INFO".
+ Similar to #2:
+
+ a. INFO -> display the remaining 60 bytes and return to #4
+
+ b. FAIL -> display the remaining 60 bytes (if present) as a failure
+ reason and consider the command failed. Stop.
+
+ c. OKAY -> success. Go to #5
+
+5. Success. Stop.
+
+
+## Example Session
+
+ Host: "getvar:version" request version variable
+
+ Client: "OKAY0.4" return version "0.4"
+
+ Host: "getvar:nonexistant" request some undefined variable
+
+ Client: "FAILUnknown variable" getvar failure; see getvar details below
+
+ Host: "download:00001234" request to send 0x1234 bytes of data
+
+ Client: "DATA00001234" ready to accept data
+
+ Host: < 0x1234 bytes > send data
+
+ Client: "OKAY" success
+
+ Host: "flash:bootloader" request to flash the data to the bootloader
+
+ Client: "INFOerasing flash" indicate status / progress
+ "INFOwriting flash"
+ "OKAY" indicate success
+
+ Host: "powerdown" send a command
+
+ Client: "FAILunknown command" indicate failure
+
+
+## Command Reference
+
+* Command parameters are indicated by printf-style escape sequences.
+
+* Commands are ascii strings and sent without the quotes (which are
+ for illustration only here) and without a trailing 0 byte.
+
+* Commands that begin with a lowercase letter are reserved for this
+ specification. OEM-specific commands should not begin with a
+ lowercase letter, to prevent incompatibilities with future specs.
+
+The various currently defined commands are:
+
+ getvar:%s Read a config/version variable from the bootloader.
+ The variable contents will be returned after the
+ OKAY response. If the variable is unknown, the bootloader
+ should return a FAIL response, optionally with an error
+ message.
+
+ Previous versions of this document indicated that getvar
+ should return an empty OKAY response for unknown
+ variables, so older devices might exhibit this behavior,
+ but new implementations should return FAIL instead.
+
+ download:%08x Write data to memory which will be later used
+ by "boot", "ramdisk", "flash", etc. The client
+ will reply with "DATA%08x" if it has enough
+ space in RAM or "FAIL" if not. The size of
+ the download is remembered.
+
+ verify:%08x Send a digital signature to verify the downloaded
+ data. Required if the bootloader is "secure"
+ otherwise "flash" and "boot" will be ignored.
+
+ flash:%s Write the previously downloaded image to the
+ named partition (if possible).
+
+ erase:%s Erase the indicated partition (clear to 0xFFs)
+
+ boot The previously downloaded data is a boot.img
+ and should be booted according to the normal
+ procedure for a boot.img
+
+ continue Continue booting as normal (if possible)
+
+ reboot Reboot the device.
+
+ reboot-bootloader
+ Reboot back into the bootloader.
+ Useful for upgrade processes that require upgrading
+ the bootloader and then upgrading other partitions
+ using the new bootloader.
+
+ powerdown Power off the device.
+
+
+
+## Client Variables
+
+The "getvar:%s" command is used to read client variables which
+represent various information about the device and the software
+on it.
+
+The various currently defined names are:
+
+ version Version of FastBoot protocol supported.
+ It should be "0.4" for this document.
+
+ version-bootloader Version string for the Bootloader.
+
+ version-baseband Version string of the Baseband Software
+
+ product Name of the product
+
+ serialno Product serial number
+
+ secure If the value is "yes", this is a secure
+ bootloader requiring a signature before
+ it will install or boot images.
+
+Names starting with a lowercase character are reserved by this
+specification. OEM-specific names should not start with lowercase
+characters.
+
+
+## TCP Protocol v1
+
+The TCP protocol is designed to be a simple way to use the fastboot protocol
+over ethernet if USB is not available.
+
+The device will open a TCP server on port 5554 and wait for a fastboot client
+to connect.
+
+### Handshake
+Upon connecting, both sides will send a 4-byte handshake message to ensure they
+are speaking the same protocol. This consists of the ASCII characters "FB"
+followed by a 2-digit base-10 ASCII version number. For example, the version 1
+handshake message will be [FB01].
+
+If either side detects a malformed handshake, it should disconnect.
+
+The protocol version to use must be the minimum of the versions sent by each
+side; if either side cannot speak this protocol version, it should disconnect.
+
+### Fastboot Data
+Once the handshake is complete, fastboot data will be sent as follows:
+
+ [data_size][data]
+
+Where data\_size is an unsigned 8-byte big-endian binary value, and data is the
+fastboot packet. The 8-byte length is intended to provide future-proofing even
+though currently fastboot packets have a 4-byte maximum length.
+
+### Example
+In this example the fastboot host queries the device for two variables,
+"version" and "none".
+
+ Host <connect to the device on port 5555>
+ Host FB01
+ Device FB01
+ Host [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0E]getvar:version
+ Device [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x07]OKAY0.4
+ Host [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0B]getvar:none
+ Device [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x14]FAILUnknown variable
+ Host <disconnect>
+
+
+## UDP Protocol v1
+
+The UDP protocol is more complex than TCP since we must implement reliability
+to ensure no packets are lost, but the general concept of wrapping the fastboot
+protocol is the same.
+
+Overview:
+ 1. As with TCP, the device will listen on UDP port 5554.
+ 2. Maximum UDP packet size is negotiated during initialization.
+ 3. The host drives all communication; the device may only send a packet as a
+ response to a host packet.
+ 4. If the host does not receive a response in 500ms it will re-transmit.
+
+### UDP Packet format
+
+ +----------+----+-------+-------+--------------------+
+ | Byte # | 0 | 1 | 2 - 3 | 4+ |
+ +----------+----+-------+-------+--------------------+
+ | Contents | ID | Flags | Seq # | Data |
+ +----------+----+-------+-------+--------------------+
+
+ ID Packet ID:
+ 0x00: Error.
+ 0x01: Query.
+ 0x02: Initialization.
+ 0x03: Fastboot.
+
+ Packet types are described in more detail below.
+
+ Flags Packet flags: 0 0 0 0 0 0 0 C
+ C=1 indicates a continuation packet; the data is too large and will
+ continue in the next packet.
+
+ Remaining bits are reserved for future use and must be set to 0.
+
+ Seq # 2-byte packet sequence number (big-endian). The host will increment
+ this by 1 with each new packet, and the device must provide the
+ corresponding sequence number in the response packets.
+
+ Data Packet data, not present in all packets.
+
+### Packet Types
+
+ Query
+ The host sends a query packet once on startup to sync with the device.
+ The host will not know the current sequence number, so the device must
+ respond to all query packets regardless of sequence number.
+
+ The response data field should contain a 2-byte big-endian value
+ giving the next expected sequence number.
+
+ Init
+ The host sends an init packet once the query response is returned. The
+ device must abort any in-progress operation and prepare for a new
+ fastboot session. This message is meant to allow recovery if a
+ previous session failed, e.g. due to network error or user Ctrl+C.
+
+ The data field contains two big-endian 2-byte values, a protocol
+ version and the max UDP packet size (including the 4-byte header).
+ Both the host and device will send these values, and in each case
+ the minimum of the sent values must be used.
+
+ Fastboot
+ These packets wrap the fastboot protocol. To write, the host will
+ send a packet with fastboot data, and the device will reply with an
+ empty packet as an ACK. To read, the host will send an empty packet,
+ and the device will reply with fastboot data. The device may not give
+ any data in the ACK packet.
+
+ Error
+ The device may respond to any packet with an error packet to indicate
+ a UDP protocol error. The data field should contain an ASCII string
+ describing the error. This is the only case where a device is allowed
+ to return a packet ID other than the one sent by the host.
+
+### Packet Size
+The maximum packet size is negotiated by the host and device in the Init packet.
+Devices must support at least 512-byte packets, but packet size has a direct
+correlation with download speed, so devices are strongly suggested to support at
+least 1024-byte packets. On a local network with 0.5ms round-trip time this will
+provide transfer rates of ~2MB/s. Over WiFi it will likely be significantly
+less.
+
+Query and Initialization packets, which are sent before size negotiation is
+complete, must always be 512 bytes or less.
+
+### Packet Re-Transmission
+The host will re-transmit any packet that does not receive a response. The
+requirement of exactly one device response packet per host packet is how we
+achieve reliability and in-order delivery of packets.
+
+For simplicity of implementation, there is no windowing of multiple
+unacknowledged packets in this version of the protocol. The host will continue
+to send the same packet until a response is received. Windowing functionality
+may be implemented in future versions if necessary to increase performance.
+
+The first Query packet will only be attempted a small number of times, but
+subsequent packets will attempt to retransmit for at least 1 minute before
+giving up. This means a device may safely ignore host UDP packets for up to 1
+minute during long operations, e.g. writing to flash.
+
+### Continuation Packets
+Any packet may set the continuation flag to indicate that the data is
+incomplete. Large data such as downloading an image may require many
+continuation packets. The receiver should respond to a continuation packet with
+an empty packet to acknowledge receipt. See examples below.
+
+### Summary
+The host starts with a Query packet, then an Initialization packet, after
+which only Fastboot packets are sent. Fastboot packets may contain data from
+the host for writes, or from the device for reads, but not both.
+
+Given a next expected sequence number S and a received packet P, the device
+behavior should be:
+
+ if P is a Query packet:
+ * respond with a Query packet with S in the data field
+ else if P has sequence == S:
+ * process P and take any required action
+ * create a response packet R with the same ID and sequence as P, containing
+ any response data required.
+ * transmit R and save it in case of re-transmission
+ * increment S
+ else if P has sequence == S - 1:
+ * re-transmit the saved response packet R from above
+ else:
+ * ignore the packet
+
+### Examples
+
+In the examples below, S indicates the starting client sequence number.
+
+ Host Client
+ ======================================================================
+ [Initialization, S = 0x55AA]
+ [Host: version 1, 2048-byte packets. Client: version 2, 1024-byte packets.]
+ [Resulting values to use: version = 1, max packet size = 1024]
+ ID Flag SeqH SeqL Data ID Flag SeqH SeqL Data
+ ----------------------------------------------------------------------
+ 0x01 0x00 0x00 0x00
+ 0x01 0x00 0x00 0x00 0x55 0xAA
+ 0x02 0x00 0x55 0xAA 0x00 0x01 0x08 0x00
+ 0x02 0x00 0x55 0xAA 0x00 0x02 0x04 0x00
+
+ ----------------------------------------------------------------------
+ [fastboot "getvar" commands, S = 0x0001]
+ ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+ ----------------------------------------------------------------------
+ 0x03 0x00 0x00 0x01 getvar:version
+ 0x03 0x00 0x00 0x01
+ 0x03 0x00 0x00 0x02
+ 0x03 0x00 0x00 0x02 OKAY0.4
+ 0x03 0x00 0x00 0x03 getvar:none
+ 0x03 0x00 0x00 0x03
+ 0x03 0x00 0x00 0x04
+ 0x03 0x00 0x00 0x04 FAILUnknown var
+
+ ----------------------------------------------------------------------
+ [fastboot "INFO" responses, S = 0x0000]
+ ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+ ----------------------------------------------------------------------
+ 0x03 0x00 0x00 0x00 <command>
+ 0x03 0x00 0x00 0x00
+ 0x03 0x00 0x00 0x01
+ 0x03 0x00 0x00 0x01 INFOWait1
+ 0x03 0x00 0x00 0x02
+ 0x03 0x00 0x00 0x02 INFOWait2
+ 0x03 0x00 0x00 0x03
+ 0x03 0x00 0x00 0x03 OKAY
+
+ ----------------------------------------------------------------------
+ [Chunking 2100 bytes of data, max packet size = 1024, S = 0xFFFF]
+ ID Flag SeqH SeqL Data ID Flag SeqH SeqL Data
+ ----------------------------------------------------------------------
+ 0x03 0x00 0xFF 0xFF download:0000834
+ 0x03 0x00 0xFF 0xFF
+ 0x03 0x00 0x00 0x00
+ 0x03 0x00 0x00 0x00 DATA0000834
+ 0x03 0x01 0x00 0x01 <1020 bytes>
+ 0x03 0x00 0x00 0x01
+ 0x03 0x01 0x00 0x02 <1020 bytes>
+ 0x03 0x00 0x00 0x02
+ 0x03 0x00 0x00 0x03 <60 bytes>
+ 0x03 0x00 0x00 0x03
+ 0x03 0x00 0x00 0x04
+ 0x03 0x00 0x00 0x04 OKAY
+
+ ----------------------------------------------------------------------
+ [Unknown ID error, S = 0x0000]
+ ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+ ----------------------------------------------------------------------
+ 0x10 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 <error message>
+
+ ----------------------------------------------------------------------
+ [Host packet loss and retransmission, S = 0x0000]
+ ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+ ----------------------------------------------------------------------
+ 0x03 0x00 0x00 0x00 getvar:version [lost]
+ 0x03 0x00 0x00 0x00 getvar:version [lost]
+ 0x03 0x00 0x00 0x00 getvar:version
+ 0x03 0x00 0x00 0x00
+ 0x03 0x00 0x00 0x01
+ 0x03 0x00 0x00 0x01 OKAY0.4
+
+ ----------------------------------------------------------------------
+ [Client packet loss and retransmission, S = 0x0000]
+ ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+ ----------------------------------------------------------------------
+ 0x03 0x00 0x00 0x00 getvar:version
+ 0x03 0x00 0x00 0x00 [lost]
+ 0x03 0x00 0x00 0x00 getvar:version
+ 0x03 0x00 0x00 0x00 [lost]
+ 0x03 0x00 0x00 0x00 getvar:version
+ 0x03 0x00 0x00 0x00
+ 0x03 0x00 0x00 0x01
+ 0x03 0x00 0x00 0x01 OKAY0.4
+
+ ----------------------------------------------------------------------
+ [Host packet delayed, S = 0x0000]
+ ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+ ----------------------------------------------------------------------
+ 0x03 0x00 0x00 0x00 getvar:version [delayed]
+ 0x03 0x00 0x00 0x00 getvar:version
+ 0x03 0x00 0x00 0x00
+ 0x03 0x00 0x00 0x01
+ 0x03 0x00 0x00 0x01 OKAY0.4
+ 0x03 0x00 0x00 0x00 getvar:version [arrives late with old seq#, is ignored]
diff --git a/fastboot/fastboot_protocol.txt b/fastboot/fastboot_protocol.txt
deleted file mode 100644
index b12e420..0000000
--- a/fastboot/fastboot_protocol.txt
+++ /dev/null
@@ -1,449 +0,0 @@
-FastBoot Version 0.4
-----------------------
-
-The fastboot protocol is a mechanism for communicating with bootloaders
-over USB or ethernet. It is designed to be very straightforward to implement,
-to allow it to be used across a wide range of devices and from hosts running
-Linux, Windows, or OSX.
-
-
-Basic Requirements
-------------------
-
-* USB
- * Two bulk endpoints (in, out) are required
- * Max packet size must be 64 bytes for full-speed, 512 bytes for
- high-speed and 1024 bytes for Super Speed USB.
- * The protocol is entirely host-driven and synchronous (unlike the
- multi-channel, bi-directional, asynchronous ADB protocol)
-
-* TCP or UDP
- * Device must be reachable via IP.
- * Device will act as the server, fastboot will be the client.
- * Fastboot data is wrapped in a simple protocol; see below for details.
-
-
-Transport and Framing
----------------------
-
-1. Host sends a command, which is an ascii string in a single
- packet no greater than 64 bytes.
-
-2. Client response with a single packet no greater than 64 bytes.
- The first four bytes of the response are "OKAY", "FAIL", "DATA",
- or "INFO". Additional bytes may contain an (ascii) informative
- message.
-
- a. INFO -> the remaining 60 bytes are an informative message
- (providing progress or diagnostic messages). They should
- be displayed and then step #2 repeats
-
- b. FAIL -> the requested command failed. The remaining 60 bytes
- of the response (if present) provide a textual failure message
- to present to the user. Stop.
-
- c. OKAY -> the requested command completed successfully. Go to #5
-
- d. DATA -> the requested command is ready for the data phase.
- A DATA response packet will be 12 bytes long, in the form of
- DATA00000000 where the 8 digit hexadecimal number represents
- the total data size to transfer.
-
-3. Data phase. Depending on the command, the host or client will
- send the indicated amount of data. Short packets are always
- acceptable and zero-length packets are ignored. This phase continues
- until the client has sent or received the number of bytes indicated
- in the "DATA" response above.
-
-4. Client responds with a single packet no greater than 64 bytes.
- The first four bytes of the response are "OKAY", "FAIL", or "INFO".
- Similar to #2:
-
- a. INFO -> display the remaining 60 bytes and return to #4
-
- b. FAIL -> display the remaining 60 bytes (if present) as a failure
- reason and consider the command failed. Stop.
-
- c. OKAY -> success. Go to #5
-
-5. Success. Stop.
-
-
-Example Session
----------------
-
-Host: "getvar:version" request version variable
-
-Client: "OKAY0.4" return version "0.4"
-
-Host: "getvar:nonexistant" request some undefined variable
-
-Client: "FAILUnknown variable" getvar failure; see getvar details below
-
-Host: "download:00001234" request to send 0x1234 bytes of data
-
-Client: "DATA00001234" ready to accept data
-
-Host: < 0x1234 bytes > send data
-
-Client: "OKAY" success
-
-Host: "flash:bootloader" request to flash the data to the bootloader
-
-Client: "INFOerasing flash" indicate status / progress
- "INFOwriting flash"
- "OKAY" indicate success
-
-Host: "powerdown" send a command
-
-Client: "FAILunknown command" indicate failure
-
-
-Command Reference
------------------
-
-* Command parameters are indicated by printf-style escape sequences.
-
-* Commands are ascii strings and sent without the quotes (which are
- for illustration only here) and without a trailing 0 byte.
-
-* Commands that begin with a lowercase letter are reserved for this
- specification. OEM-specific commands should not begin with a
- lowercase letter, to prevent incompatibilities with future specs.
-
- "getvar:%s" Read a config/version variable from the bootloader.
- The variable contents will be returned after the
- OKAY response. If the variable is unknown, the bootloader
- should return a FAIL response, optionally with an error
- message.
-
- Previous versions of this document indicated that getvar
- should return an empty OKAY response for unknown
- variables, so older devices might exhibit this behavior,
- but new implementations should return FAIL instead.
-
- "download:%08x" Write data to memory which will be later used
- by "boot", "ramdisk", "flash", etc. The client
- will reply with "DATA%08x" if it has enough
- space in RAM or "FAIL" if not. The size of
- the download is remembered.
-
- "verify:%08x" Send a digital signature to verify the downloaded
- data. Required if the bootloader is "secure"
- otherwise "flash" and "boot" will be ignored.
-
- "flash:%s" Write the previously downloaded image to the
- named partition (if possible).
-
- "erase:%s" Erase the indicated partition (clear to 0xFFs)
-
- "boot" The previously downloaded data is a boot.img
- and should be booted according to the normal
- procedure for a boot.img
-
- "continue" Continue booting as normal (if possible)
-
- "reboot" Reboot the device.
-
- "reboot-bootloader" Reboot back into the bootloader.
- Useful for upgrade processes that require upgrading
- the bootloader and then upgrading other partitions
- using the new bootloader.
-
- "powerdown" Power off the device.
-
-
-
-Client Variables
-----------------
-
-The "getvar:%s" command is used to read client variables which
-represent various information about the device and the software
-on it.
-
-The various currently defined names are:
-
- version Version of FastBoot protocol supported.
- It should be "0.4" for this document.
-
- version-bootloader Version string for the Bootloader.
-
- version-baseband Version string of the Baseband Software
-
- product Name of the product
-
- serialno Product serial number
-
- secure If the value is "yes", this is a secure
- bootloader requiring a signature before
- it will install or boot images.
-
-Names starting with a lowercase character are reserved by this
-specification. OEM-specific names should not start with lowercase
-characters.
-
-
-TCP Protocol v1
----------------
-
-The TCP protocol is designed to be a simple way to use the fastboot protocol
-over ethernet if USB is not available.
-
-The device will open a TCP server on port 5554 and wait for a fastboot client
-to connect.
-
--- Handshake --
-Upon connecting, both sides will send a 4-byte handshake message to ensure they
-are speaking the same protocol. This consists of the ASCII characters "FB"
-followed by a 2-digit base-10 ASCII version number. For example, the version 1
-handshake message will be [FB01].
-
-If either side detects a malformed handshake, it should disconnect.
-
-The protocol version to use must be the minimum of the versions sent by each
-side; if either side cannot speak this protocol version, it should disconnect.
-
--- Fastboot Data --
-Once the handshake is complete, fastboot data will be sent as follows:
-
- [data_size][data]
-
-Where data_size is an unsigned 8-byte big-endian binary value, and data is the
-fastboot packet. The 8-byte length is intended to provide future-proofing even
-though currently fastboot packets have a 4-byte maximum length.
-
--- Example --
-In this example the fastboot host queries the device for two variables,
-"version" and "none".
-
-Host <connect to the device on port 5555>
-Host FB01
-Device FB01
-Host [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0E]getvar:version
-Device [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x07]OKAY0.4
-Host [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0B]getvar:none
-Device [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x14]FAILUnknown variable
-Host <disconnect>
-
-
-UDP Protocol v1
----------------
-
-The UDP protocol is more complex than TCP since we must implement reliability
-to ensure no packets are lost, but the general concept of wrapping the fastboot
-protocol is the same.
-
-Overview:
- 1. As with TCP, the device will listen on UDP port 5554.
- 2. Maximum UDP packet size is negotiated during initialization.
- 3. The host drives all communication; the device may only send a packet as a
- response to a host packet.
- 4. If the host does not receive a response in 500ms it will re-transmit.
-
--- UDP Packet format --
- +----------+----+-------+-------+--------------------+
- | Byte # | 0 | 1 | 2 - 3 | 4+ |
- +----------+----+-------+-------+--------------------+
- | Contents | ID | Flags | Seq # | Data |
- +----------+----+-------+-------+--------------------+
-
- ID Packet ID:
- 0x00: Error.
- 0x01: Query.
- 0x02: Initialization.
- 0x03: Fastboot.
-
- Packet types are described in more detail below.
-
- Flags Packet flags: 0 0 0 0 0 0 0 C
- C=1 indicates a continuation packet; the data is too large and will
- continue in the next packet.
-
- Remaining bits are reserved for future use and must be set to 0.
-
- Seq # 2-byte packet sequence number (big-endian). The host will increment
- this by 1 with each new packet, and the device must provide the
- corresponding sequence number in the response packets.
-
- Data Packet data, not present in all packets.
-
--- Packet Types --
-Query The host sends a query packet once on startup to sync with the device.
- The host will not know the current sequence number, so the device must
- respond to all query packets regardless of sequence number.
-
- The response data field should contain a 2-byte big-endian value
- giving the next expected sequence number.
-
-Init The host sends an init packet once the query response is returned. The
- device must abort any in-progress operation and prepare for a new
- fastboot session. This message is meant to allow recovery if a
- previous session failed, e.g. due to network error or user Ctrl+C.
-
- The data field contains two big-endian 2-byte values, a protocol
- version and the max UDP packet size (including the 4-byte header).
- Both the host and device will send these values, and in each case
- the minimum of the sent values must be used.
-
-Fastboot These packets wrap the fastboot protocol. To write, the host will
- send a packet with fastboot data, and the device will reply with an
- empty packet as an ACK. To read, the host will send an empty packet,
- and the device will reply with fastboot data. The device may not give
- any data in the ACK packet.
-
-Error The device may respond to any packet with an error packet to indicate
- a UDP protocol error. The data field should contain an ASCII string
- describing the error. This is the only case where a device is allowed
- to return a packet ID other than the one sent by the host.
-
--- Packet Size --
-The maximum packet size is negotiated by the host and device in the Init packet.
-Devices must support at least 512-byte packets, but packet size has a direct
-correlation with download speed, so devices are strongly suggested to support at
-least 1024-byte packets. On a local network with 0.5ms round-trip time this will
-provide transfer rates of ~2MB/s. Over WiFi it will likely be significantly
-less.
-
-Query and Initialization packets, which are sent before size negotiation is
-complete, must always be 512 bytes or less.
-
--- Packet Re-Transmission --
-The host will re-transmit any packet that does not receive a response. The
-requirement of exactly one device response packet per host packet is how we
-achieve reliability and in-order delivery of packets.
-
-For simplicity of implementation, there is no windowing of multiple
-unacknowledged packets in this version of the protocol. The host will continue
-to send the same packet until a response is received. Windowing functionality
-may be implemented in future versions if necessary to increase performance.
-
-The first Query packet will only be attempted a small number of times, but
-subsequent packets will attempt to retransmit for at least 1 minute before
-giving up. This means a device may safely ignore host UDP packets for up to 1
-minute during long operations, e.g. writing to flash.
-
--- Continuation Packets --
-Any packet may set the continuation flag to indicate that the data is
-incomplete. Large data such as downloading an image may require many
-continuation packets. The receiver should respond to a continuation packet with
-an empty packet to acknowledge receipt. See examples below.
-
--- Summary --
-The host starts with a Query packet, then an Initialization packet, after
-which only Fastboot packets are sent. Fastboot packets may contain data from
-the host for writes, or from the device for reads, but not both.
-
-Given a next expected sequence number S and a received packet P, the device
-behavior should be:
- if P is a Query packet:
- * respond with a Query packet with S in the data field
- else if P has sequence == S:
- * process P and take any required action
- * create a response packet R with the same ID and sequence as P, containing
- any response data required.
- * transmit R and save it in case of re-transmission
- * increment S
- else if P has sequence == S - 1:
- * re-transmit the saved response packet R from above
- else:
- * ignore the packet
-
--- Examples --
-In the examples below, S indicates the starting client sequence number.
-
-Host Client
-======================================================================
-[Initialization, S = 0x55AA]
-[Host: version 1, 2048-byte packets. Client: version 2, 1024-byte packets.]
-[Resulting values to use: version = 1, max packet size = 1024]
-ID Flag SeqH SeqL Data ID Flag SeqH SeqL Data
-----------------------------------------------------------------------
-0x01 0x00 0x00 0x00
- 0x01 0x00 0x00 0x00 0x55 0xAA
-0x02 0x00 0x55 0xAA 0x00 0x01 0x08 0x00
- 0x02 0x00 0x55 0xAA 0x00 0x02 0x04 0x00
-
-----------------------------------------------------------------------
-[fastboot "getvar" commands, S = 0x0001]
-ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
-----------------------------------------------------------------------
-0x03 0x00 0x00 0x01 getvar:version
- 0x03 0x00 0x00 0x01
-0x03 0x00 0x00 0x02
- 0x03 0x00 0x00 0x02 OKAY0.4
-0x03 0x00 0x00 0x03 getvar:none
- 0x03 0x00 0x00 0x03
-0x03 0x00 0x00 0x04
- 0x03 0x00 0x00 0x04 FAILUnknown var
-
-----------------------------------------------------------------------
-[fastboot "INFO" responses, S = 0x0000]
-ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
-----------------------------------------------------------------------
-0x03 0x00 0x00 0x00 <command>
- 0x03 0x00 0x00 0x00
-0x03 0x00 0x00 0x01
- 0x03 0x00 0x00 0x01 INFOWait1
-0x03 0x00 0x00 0x02
- 0x03 0x00 0x00 0x02 INFOWait2
-0x03 0x00 0x00 0x03
- 0x03 0x00 0x00 0x03 OKAY
-
-----------------------------------------------------------------------
-[Chunking 2100 bytes of data, max packet size = 1024, S = 0xFFFF]
-ID Flag SeqH SeqL Data ID Flag SeqH SeqL Data
-----------------------------------------------------------------------
-0x03 0x00 0xFF 0xFF download:0000834
- 0x03 0x00 0xFF 0xFF
-0x03 0x00 0x00 0x00
- 0x03 0x00 0x00 0x00 DATA0000834
-0x03 0x01 0x00 0x01 <1020 bytes>
- 0x03 0x00 0x00 0x01
-0x03 0x01 0x00 0x02 <1020 bytes>
- 0x03 0x00 0x00 0x02
-0x03 0x00 0x00 0x03 <60 bytes>
- 0x03 0x00 0x00 0x03
-0x03 0x00 0x00 0x04
- 0x03 0x00 0x00 0x04 OKAY
-
-----------------------------------------------------------------------
-[Unknown ID error, S = 0x0000]
-ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
-----------------------------------------------------------------------
-0x10 0x00 0x00 0x00
- 0x00 0x00 0x00 0x00 <error message>
-
-----------------------------------------------------------------------
-[Host packet loss and retransmission, S = 0x0000]
-ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
-----------------------------------------------------------------------
-0x03 0x00 0x00 0x00 getvar:version [lost]
-0x03 0x00 0x00 0x00 getvar:version [lost]
-0x03 0x00 0x00 0x00 getvar:version
- 0x03 0x00 0x00 0x00
-0x03 0x00 0x00 0x01
- 0x03 0x00 0x00 0x01 OKAY0.4
-
-----------------------------------------------------------------------
-[Client packet loss and retransmission, S = 0x0000]
-ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
-----------------------------------------------------------------------
-0x03 0x00 0x00 0x00 getvar:version
- 0x03 0x00 0x00 0x00 [lost]
-0x03 0x00 0x00 0x00 getvar:version
- 0x03 0x00 0x00 0x00 [lost]
-0x03 0x00 0x00 0x00 getvar:version
- 0x03 0x00 0x00 0x00
-0x03 0x00 0x00 0x01
- 0x03 0x00 0x00 0x01 OKAY0.4
-
-----------------------------------------------------------------------
-[Host packet delayed, S = 0x0000]
-ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
-----------------------------------------------------------------------
-0x03 0x00 0x00 0x00 getvar:version [delayed]
-0x03 0x00 0x00 0x00 getvar:version
- 0x03 0x00 0x00 0x00
-0x03 0x00 0x00 0x01
- 0x03 0x00 0x00 0x01 OKAY0.4
-0x03 0x00 0x00 0x00 getvar:version [arrives late with old seq#, is ignored]
diff --git a/fingerprintd/fingerprintd.cpp b/fingerprintd/fingerprintd.cpp
index 05109b7..2fc2d0a 100644
--- a/fingerprintd/fingerprintd.cpp
+++ b/fingerprintd/fingerprintd.cpp
@@ -16,7 +16,6 @@
#define LOG_TAG "fingerprintd"
-#include <android/log.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
@@ -25,6 +24,7 @@
#include <hardware/hw_auth_token.h>
#include <keystore/IKeystoreService.h>
#include <keystore/keystore.h> // for error codes
+#include <log/log.h>
#include <utils/Log.h>
#include <utils/String16.h>
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
index d6b699b..8d5b51b 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -9,7 +9,7 @@
libbase \
libcrypto_utils \
libcrypto \
- libext4_utils_static \
+ libext4_utils \
libsquashfs_utils \
libselinux
@@ -18,6 +18,7 @@
LOCAL_SANITIZE := integer
LOCAL_SRC_FILES:= \
fs_mgr.c \
+ fs_mgr_dm_ioctl.cpp \
fs_mgr_format.c \
fs_mgr_fstab.c \
fs_mgr_slotselect.c \
@@ -26,12 +27,16 @@
$(LOCAL_PATH)/include \
system/vold \
system/extras/ext4_utils \
- external/openssl/include \
bootable/recovery
LOCAL_MODULE:= libfs_mgr
LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_CFLAGS := -Werror
+ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
+ ifeq ($(TARGET_USES_MKE2FS), true)
+ LOCAL_CFLAGS += -DTARGET_USES_MKE2FS
+ endif
+endif
ifneq (,$(filter userdebug,$(TARGET_BUILD_VARIANT)))
LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
endif
@@ -52,7 +57,7 @@
libcutils \
liblog \
libc \
- libsparse_static \
+ libsparse \
libz \
libselinux
LOCAL_CXX_STL := libc++_static
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index ba44a5a..9a53d62 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -101,7 +101,9 @@
char tmpmnt_opts[64] = "errors=remount-ro";
char *e2fsck_argv[] = {
E2FSCK_BIN,
+#ifndef TARGET_USES_MKE2FS // "-f" only for old ext4 generation tool
"-f",
+#endif
"-y",
blk_device
};
@@ -644,6 +646,17 @@
}
}
+int fs_mgr_test_access(const char *device) {
+ int tries = 25;
+ while (tries--) {
+ if (!access(device, F_OK) || errno != ENOENT) {
+ return 0;
+ }
+ usleep(40 * 1000);
+ }
+ return -1;
+}
+
/* 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.
@@ -701,7 +714,7 @@
}
if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
- int rc = fs_mgr_setup_verity(&fstab->recs[i]);
+ int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
INFO("Verity disabled");
} else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
@@ -870,7 +883,7 @@
}
if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
- int rc = fs_mgr_setup_verity(&fstab->recs[i]);
+ int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
INFO("Verity disabled");
} else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
@@ -1086,7 +1099,7 @@
int fs_mgr_early_setup_verity(struct fstab_rec *fstab_rec)
{
if ((fstab_rec->fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
- int rc = fs_mgr_setup_verity(fstab_rec);
+ int rc = fs_mgr_setup_verity(fstab_rec, false);
if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
INFO("Verity disabled");
return FS_MGR_EARLY_SETUP_VERITY_NO_VERITY;
diff --git a/fs_mgr/fs_mgr_dm_ioctl.cpp b/fs_mgr/fs_mgr_dm_ioctl.cpp
new file mode 100644
index 0000000..75ce621
--- /dev/null
+++ b/fs_mgr/fs_mgr_dm_ioctl.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 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 <string.h>
+
+#include <android-base/logging.h>
+#include <sys/ioctl.h>
+
+#include "fs_mgr_priv.h"
+#include "fs_mgr_priv_dm_ioctl.h"
+
+void fs_mgr_verity_ioctl_init(struct dm_ioctl *io,
+ const std::string &name,
+ unsigned flags)
+{
+ memset(io, 0, DM_BUF_SIZE);
+ io->data_size = DM_BUF_SIZE;
+ io->data_start = sizeof(struct dm_ioctl);
+ io->version[0] = 4;
+ io->version[1] = 0;
+ io->version[2] = 0;
+ io->flags = flags | DM_READONLY_FLAG;
+ if (!name.empty()) {
+ strlcpy(io->name, name.c_str(), sizeof(io->name));
+ }
+}
+
+bool fs_mgr_create_verity_device(struct dm_ioctl *io,
+ const std::string &name,
+ int fd)
+{
+ fs_mgr_verity_ioctl_init(io, name, 1);
+ if (ioctl(fd, DM_DEV_CREATE, io)) {
+ ERROR("Error creating device mapping (%s)", strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+bool fs_mgr_destroy_verity_device(struct dm_ioctl *io,
+ const std::string &name,
+ int fd)
+{
+ fs_mgr_verity_ioctl_init(io, name, 0);
+ if (ioctl(fd, DM_DEV_REMOVE, io)) {
+ ERROR("Error removing device mapping (%s)", strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+bool fs_mgr_get_verity_device_name(struct dm_ioctl *io,
+ const std::string &name,
+ int fd,
+ std::string *out_dev_name)
+{
+ CHECK(out_dev_name != nullptr);
+
+ fs_mgr_verity_ioctl_init(io, name, 0);
+ if (ioctl(fd, DM_DEV_STATUS, io)) {
+ ERROR("Error fetching verity device number (%s)", strerror(errno));
+ return false;
+ }
+
+ int dev_num = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
+ *out_dev_name = "/dev/block/dm-" + std::to_string(dev_num);
+
+ return true;
+}
+
+bool fs_mgr_resume_verity_table(struct dm_ioctl *io,
+ const std::string &name,
+ int fd)
+{
+ fs_mgr_verity_ioctl_init(io, name, 0);
+ if (ioctl(fd, DM_DEV_SUSPEND, io)) {
+ ERROR("Error activating verity device (%s)", strerror(errno));
+ return false;
+ }
+ return true;
+}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index db86afa..7f917d9 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -93,6 +93,7 @@
#define DM_BUF_SIZE 4096
int fs_mgr_set_blk_ro(const char *blockdev);
+int fs_mgr_test_access(const char *device);
int fs_mgr_update_for_slotselect(struct fstab *fstab);
__END_DECLS
diff --git a/fs_mgr/fs_mgr_priv_dm_ioctl.h b/fs_mgr/fs_mgr_priv_dm_ioctl.h
new file mode 100644
index 0000000..eeae4dd
--- /dev/null
+++ b/fs_mgr/fs_mgr_priv_dm_ioctl.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 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 __CORE_FS_MGR_PRIV_DM_IOCTL_H
+#define __CORE_FS_MGR_PRIV_DM_IOCTL_H
+
+#include <string>
+#include <linux/dm-ioctl.h>
+
+void fs_mgr_verity_ioctl_init(struct dm_ioctl *io,
+ const std::string &name,
+ unsigned flags);
+
+bool fs_mgr_create_verity_device(struct dm_ioctl *io,
+ const std::string &name,
+ int fd);
+
+bool fs_mgr_destroy_verity_device(struct dm_ioctl *io,
+ const std::string &name,
+ int fd);
+
+bool fs_mgr_get_verity_device_name(struct dm_ioctl *io,
+ const std::string &name,
+ int fd,
+ std::string *out_dev_name);
+
+bool fs_mgr_resume_verity_table(struct dm_ioctl *io,
+ const std::string &name,
+ int fd);
+
+#endif /* __CORE_FS_MGR_PRIV_DM_IOCTL_H */
diff --git a/fs_mgr/fs_mgr_priv_verity.h b/fs_mgr/fs_mgr_priv_verity.h
index d9e17bb..1a6d215 100644
--- a/fs_mgr/fs_mgr_priv_verity.h
+++ b/fs_mgr/fs_mgr_priv_verity.h
@@ -22,6 +22,6 @@
__BEGIN_DECLS
-int fs_mgr_setup_verity(struct fstab_rec *fstab);
+int fs_mgr_setup_verity(struct fstab_rec *fstab, bool verify_dev);
__END_DECLS
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 031b042..e368a82 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -31,6 +31,7 @@
#include <android-base/file.h>
#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
#include <crypto_utils/android_pubkey.h>
#include <cutils/properties.h>
#include <logwrap/logwrap.h>
@@ -43,6 +44,7 @@
#include "fs_mgr.h"
#include "fs_mgr_priv.h"
+#include "fs_mgr_priv_dm_ioctl.h"
#include "fs_mgr_priv_verity.h"
#define FSTAB_PREFIX "/fstab."
@@ -73,6 +75,8 @@
#define VERITY_KMSG_RESTART "dm-verity device corrupted"
#define VERITY_KMSG_BUFSIZE 1024
+#define READ_BUF_SIZE 4096
+
#define __STRINGIFY(x) #x
#define STRINGIFY(x) __STRINGIFY(x)
@@ -181,45 +185,6 @@
return -1;
}
-static void verity_ioctl_init(struct dm_ioctl *io, const char *name, unsigned flags)
-{
- memset(io, 0, DM_BUF_SIZE);
- io->data_size = DM_BUF_SIZE;
- io->data_start = sizeof(struct dm_ioctl);
- io->version[0] = 4;
- io->version[1] = 0;
- io->version[2] = 0;
- io->flags = flags | DM_READONLY_FLAG;
- if (name) {
- strlcpy(io->name, name, sizeof(io->name));
- }
-}
-
-static int create_verity_device(struct dm_ioctl *io, char *name, int fd)
-{
- verity_ioctl_init(io, name, 1);
- if (ioctl(fd, DM_DEV_CREATE, io)) {
- ERROR("Error creating device mapping (%s)", strerror(errno));
- return -1;
- }
- return 0;
-}
-
-static int get_verity_device_name(struct dm_ioctl *io, char *name, int fd, char **dev_name)
-{
- verity_ioctl_init(io, name, 0);
- if (ioctl(fd, DM_DEV_STATUS, io)) {
- ERROR("Error fetching verity device number (%s)", strerror(errno));
- return -1;
- }
- int dev_num = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
- if (asprintf(dev_name, "/dev/block/dm-%u", dev_num) < 0) {
- ERROR("Error getting verity block device name (%s)", strerror(errno));
- return -1;
- }
- return 0;
-}
-
struct verity_table_params {
char *table;
int mode;
@@ -288,14 +253,15 @@
return true;
}
-static int load_verity_table(struct dm_ioctl *io, char *name, uint64_t device_size, int fd,
+static int load_verity_table(struct dm_ioctl *io, const std::string &name,
+ uint64_t device_size, int fd,
const struct verity_table_params *params, format_verity_table_func format)
{
char *verity_params;
char *buffer = (char*) io;
size_t bufsize;
- verity_ioctl_init(io, name, DM_STATUS_TABLE_FLAG);
+ fs_mgr_verity_ioctl_init(io, name, DM_STATUS_TABLE_FLAG);
struct dm_target_spec *tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
@@ -331,27 +297,6 @@
return 0;
}
-static int resume_verity_table(struct dm_ioctl *io, char *name, int fd)
-{
- verity_ioctl_init(io, name, 0);
- if (ioctl(fd, DM_DEV_SUSPEND, io)) {
- ERROR("Error activating verity device (%s)", strerror(errno));
- return -1;
- }
- return 0;
-}
-
-static int test_access(char *device) {
- int tries = 25;
- while (tries--) {
- if (!access(device, F_OK) || errno != ENOENT) {
- return 0;
- }
- usleep(40 * 1000);
- }
- return -1;
-}
-
static int check_verity_restart(const char *fname)
{
char buffer[VERITY_KMSG_BUFSIZE + 1];
@@ -606,6 +551,30 @@
return rc;
}
+static int read_partition(const char *path, uint64_t size)
+{
+ char buf[READ_BUF_SIZE];
+ ssize_t size_read;
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC)));
+
+ if (fd == -1) {
+ ERROR("Failed to open %s: %s\n", path, strerror(errno));
+ return -errno;
+ }
+
+ while (size) {
+ size_read = TEMP_FAILURE_RETRY(read(fd, buf, READ_BUF_SIZE));
+ if (size_read == -1) {
+ ERROR("Error in reading partition %s: %s\n", path,
+ strerror(errno));
+ return -errno;
+ }
+ size -= size_read;
+ }
+
+ return 0;
+}
+
static int compare_last_signature(struct fstab_rec *fstab, int *match)
{
char tag[METADATA_TAG_MAX_LENGTH + 1];
@@ -786,9 +755,9 @@
alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
bool system_root = false;
char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
- const char *mount_point;
+ std::string mount_point;
char propbuf[PROPERTY_VALUE_MAX];
- char *status;
+ const char *status;
int fd = -1;
int i;
int mode;
@@ -835,18 +804,22 @@
mount_point = basename(fstab->recs[i].mount_point);
}
- verity_ioctl_init(io, mount_point, 0);
+ fs_mgr_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));
- continue;
+ if (fstab->recs[i].fs_mgr_flags & MF_VERIFYATBOOT) {
+ status = "V";
+ } else {
+ ERROR("Failed to query DM_TABLE_STATUS for %s (%s)\n",
+ mount_point.c_str(), strerror(errno));
+ continue;
+ }
}
status = &buffer[io->data_start + sizeof(struct dm_target_spec)];
if (*status == 'C' || *status == 'V') {
- callback(&fstab->recs[i], mount_point, mode, *status);
+ callback(&fstab->recs[i], mount_point.c_str(), mode, *status);
}
}
@@ -892,18 +865,19 @@
*table = strdup(result.c_str());
}
-int fs_mgr_setup_verity(struct fstab_rec *fstab)
+int fs_mgr_setup_verity(struct fstab_rec *fstab, bool verify_dev)
{
int retval = FS_MGR_SETUP_VERITY_FAIL;
int fd = -1;
- char *verity_blk_name = NULL;
+ std::string verity_blk_name;
struct fec_handle *f = NULL;
struct fec_verity_metadata verity;
struct verity_table_params params = { .table = NULL };
alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
struct dm_ioctl *io = (struct dm_ioctl *) buffer;
- char *mount_point = basename(fstab->mount_point);
+ const std::string mount_point(basename(fstab->mount_point));
+ bool verified_at_boot = false;
if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
FEC_DEFAULT_ROOTS) < 0) {
@@ -941,13 +915,13 @@
}
// create the device
- if (create_verity_device(io, mount_point, fd) < 0) {
+ if (!fs_mgr_create_verity_device(io, mount_point, fd)) {
ERROR("Couldn't create verity device!\n");
goto out;
}
// get the name of the device file
- if (get_verity_device_name(io, mount_point, fd, &verity_blk_name) < 0) {
+ if (!fs_mgr_get_verity_device_name(io, mount_point, fd, &verity_blk_name)) {
ERROR("Couldn't get verity device number!\n");
goto out;
}
@@ -983,7 +957,8 @@
}
}
- INFO("Enabling dm-verity for %s (mode %d)\n", mount_point, params.mode);
+ INFO("Enabling dm-verity for %s (mode %d)\n",
+ mount_point.c_str(), params.mode);
if (fstab->fs_mgr_flags & MF_SLOTSELECT) {
// Update the verity params using the actual block device path
@@ -998,7 +973,7 @@
if (params.ecc.valid) {
// kernel may not support error correction, try without
- INFO("Disabling error correction for %s\n", mount_point);
+ INFO("Disabling error correction for %s\n", mount_point.c_str());
params.ecc.valid = false;
if (load_verity_table(io, mount_point, verity.data_size, fd, ¶ms,
@@ -1015,7 +990,7 @@
if (params.mode != VERITY_MODE_EIO) {
// as a last resort, EIO mode should always be supported
- INFO("Falling back to EIO mode for %s\n", mount_point);
+ INFO("Falling back to EIO mode for %s\n", mount_point.c_str());
params.mode = VERITY_MODE_EIO;
if (load_verity_table(io, mount_point, verity.data_size, fd, ¶ms,
@@ -1024,26 +999,41 @@
}
}
- ERROR("Failed to load verity table for %s\n", mount_point);
+ ERROR("Failed to load verity table for %s\n", mount_point.c_str());
goto out;
loaded:
// activate the device
- if (resume_verity_table(io, mount_point, fd) < 0) {
+ if (!fs_mgr_resume_verity_table(io, mount_point, fd)) {
goto out;
}
// mark the underlying block device as read-only
fs_mgr_set_blk_ro(fstab->blk_device);
+ // Verify the entire partition in one go
+ // If there is an error, allow it to mount as a normal verity partition.
+ if (fstab->fs_mgr_flags & MF_VERIFYATBOOT) {
+ INFO("Verifying partition %s at boot\n", fstab->blk_device);
+ int err = read_partition(verity_blk_name.c_str(), verity.data_size);
+ if (!err) {
+ INFO("Verified verity partition %s at boot\n", fstab->blk_device);
+ verified_at_boot = true;
+ }
+ }
+
// assign the new verity block device as the block device
- free(fstab->blk_device);
- fstab->blk_device = verity_blk_name;
- verity_blk_name = 0;
+ if (!verified_at_boot) {
+ free(fstab->blk_device);
+ fstab->blk_device = strdup(verity_blk_name.c_str());
+ } else if (!fs_mgr_destroy_verity_device(io, mount_point, fd)) {
+ ERROR("Failed to remove verity device %s\n", mount_point.c_str());
+ goto out;
+ }
// make sure we've set everything up properly
- if (test_access(fstab->blk_device) < 0) {
+ if (verify_dev && fs_mgr_test_access(fstab->blk_device) < 0) {
goto out;
}
@@ -1056,7 +1046,6 @@
fec_close(f);
free(params.table);
- free(verity_blk_name);
return retval;
}
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 4107f55..d4a92e5 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -24,7 +24,6 @@
#include <stdint.h>
#include <unistd.h>
-#include <android/log.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
@@ -33,6 +32,7 @@
#include <hardware/hw_auth_token.h>
#include <keystore/IKeystoreService.h>
#include <keystore/keystore.h> // For error code
+#include <log/log.h>
#include <utils/Log.h>
#include <utils/String16.h>
diff --git a/include/cutils/debugger.h b/include/cutils/debugger.h
deleted file mode 100644
index 20e8796..0000000
--- a/include/cutils/debugger.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef __CUTILS_DEBUGGER_H
-#define __CUTILS_DEBUGGER_H
-
-#include <sys/cdefs.h>
-#include <sys/types.h>
-
-#include "debuggerd/client.h"
-
-__BEGIN_DECLS
-
-/* Dumps a process backtrace, registers, and stack to a tombstone file (requires root).
- * Stores the tombstone path in the provided buffer.
- * Returns 0 on success, -1 on error.
- */
-int dump_tombstone(pid_t tid, char* pathbuf, size_t pathlen);
-
-/* Dumps a process backtrace, registers, and stack to a tombstone file (requires root).
- * Stores the tombstone path in the provided buffer.
- * If reading debugger data from debuggerd ever takes longer than timeout_secs
- * seconds, then stop and return an error.
- * Returns 0 on success, -1 on error.
- */
-int dump_tombstone_timeout(pid_t tid, char* pathbuf, size_t pathlen, int timeout_secs);
-
-/* Dumps a process backtrace only to the specified file (requires root).
- * Returns 0 on success, -1 on error.
- */
-int dump_backtrace_to_file(pid_t tid, int fd);
-
-/* Dumps a process backtrace only to the specified file (requires root).
- * If reading debugger data from debuggerd ever takes longer than timeout_secs
- * seconds, then stop and return an error.
- * Returns 0 on success, -1 on error.
- */
-int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs);
-
-__END_DECLS
-
-#endif /* __CUTILS_DEBUGGER_H */
diff --git a/include/cutils/klog.h b/include/cutils/klog.h
index c837edb..e7cd300 100644
--- a/include/cutils/klog.h
+++ b/include/cutils/klog.h
@@ -44,6 +44,4 @@
#define KLOG_INFO(tag,x...) klog_write(KLOG_INFO_LEVEL, "<6>" tag ": " x)
#define KLOG_DEBUG(tag,x...) klog_write(KLOG_DEBUG_LEVEL, "<7>" tag ": " x)
-#define KLOG_DEFAULT_LEVEL 3 /* messages <= this level are logged */
-
#endif
diff --git a/include/cutils/multiuser.h b/include/cutils/multiuser.h
index 4f23776..5bd9c7b 100644
--- a/include/cutils/multiuser.h
+++ b/include/cutils/multiuser.h
@@ -32,6 +32,7 @@
extern uid_t multiuser_get_uid(userid_t user_id, appid_t app_id);
extern gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id);
+extern gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id);
extern gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id);
/* TODO: switch callers over to multiuser_get_shared_gid() */
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index f7cf9b8..8e2bc1c 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -126,6 +126,8 @@
#define AID_MEDIA_AUDIO 1055 /* GID for audio files on internal media storage */
#define AID_MEDIA_VIDEO 1056 /* GID for video files on internal media storage */
#define AID_MEDIA_IMAGE 1057 /* GID for image files on internal media storage */
+#define AID_TOMBSTONED 1058 /* tombstoned user */
+#define AID_MEDIA_OBB 1059 /* GID for OBB files on internal media storage */
/* Changes to this file must be made in AOSP, *not* in internal branches. */
#define AID_SHELL 2000 /* adb and debug shell user */
@@ -164,6 +166,9 @@
#define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */
#define AID_CACHE_GID_END 29999 /* end of gids for apps to mark cached data */
+#define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */
+#define AID_EXT_GID_END 39999 /* end of gids for apps to mark external data */
+
#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
#define AID_SHARED_GID_END 59999 /* end of gids for apps in each user to share */
@@ -173,104 +178,15 @@
#define AID_USER 100000 /* TODO: switch users over to AID_USER_OFFSET */
#define AID_USER_OFFSET 100000 /* offset for uid ranges for each user */
-#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
+ * android_ids has moved to pwd/grp functionality.
+ * If you need to add one, the structure is now
+ * auto-generated based on the AID_ constraints
+ * documented at the top of this header file.
+ * Also see build/tools/fs_config for more details.
*/
-struct android_id_info {
- const char *name;
- unsigned aid;
-};
-static const struct android_id_info android_ids[] = {
- { "root", AID_ROOT, },
-
- { "system", AID_SYSTEM, },
-
- { "radio", AID_RADIO, },
- { "bluetooth", AID_BLUETOOTH, },
- { "graphics", AID_GRAPHICS, },
- { "input", AID_INPUT, },
- { "audio", AID_AUDIO, },
- { "camera", AID_CAMERA, },
- { "log", AID_LOG, },
- { "compass", AID_COMPASS, },
- { "mount", AID_MOUNT, },
- { "wifi", AID_WIFI, },
- { "adb", AID_ADB, },
- { "install", AID_INSTALL, },
- { "media", AID_MEDIA, },
- { "dhcp", AID_DHCP, },
- { "sdcard_rw", AID_SDCARD_RW, },
- { "vpn", AID_VPN, },
- { "keystore", AID_KEYSTORE, },
- { "usb", AID_USB, },
- { "drm", AID_DRM, },
- { "mdnsr", AID_MDNSR, },
- { "gps", AID_GPS, },
- // AID_UNUSED1
- { "media_rw", AID_MEDIA_RW, },
- { "mtp", AID_MTP, },
- // AID_UNUSED2
- { "drmrpc", AID_DRMRPC, },
- { "nfc", AID_NFC, },
- { "sdcard_r", AID_SDCARD_R, },
- { "clat", AID_CLAT, },
- { "loop_radio", AID_LOOP_RADIO, },
- { "mediadrm", AID_MEDIA_DRM, },
- { "package_info", AID_PACKAGE_INFO, },
- { "sdcard_pics", AID_SDCARD_PICS, },
- { "sdcard_av", AID_SDCARD_AV, },
- { "sdcard_all", AID_SDCARD_ALL, },
- { "logd", AID_LOGD, },
- { "shared_relro", AID_SHARED_RELRO, },
- { "dbus", AID_DBUS, },
- { "tlsdate", AID_TLSDATE, },
- { "mediaex", AID_MEDIA_EX, },
- { "audioserver", AID_AUDIOSERVER, },
- { "metrics_coll", AID_METRICS_COLL },
- { "metricsd", AID_METRICSD },
- { "webserv", AID_WEBSERV },
- { "debuggerd", AID_DEBUGGERD, },
- { "mediacodec", AID_MEDIA_CODEC, },
- { "cameraserver", AID_CAMERASERVER, },
- { "firewall", AID_FIREWALL, },
- { "trunks", AID_TRUNKS, },
- { "nvram", AID_NVRAM, },
- { "dns", AID_DNS, },
- { "dns_tether", AID_DNS_TETHER, },
- { "webview_zygote", AID_WEBVIEW_ZYGOTE, },
- { "vehicle_network", AID_VEHICLE_NETWORK, },
- { "media_audio", AID_MEDIA_AUDIO, },
- { "media_video", AID_MEDIA_VIDEO, },
- { "media_image", AID_MEDIA_IMAGE, },
-
- { "shell", AID_SHELL, },
- { "cache", AID_CACHE, },
- { "diag", AID_DIAG, },
-
- { "net_bt_admin", AID_NET_BT_ADMIN, },
- { "net_bt", AID_NET_BT, },
- { "inet", AID_INET, },
- { "net_raw", AID_NET_RAW, },
- { "net_admin", AID_NET_ADMIN, },
- { "net_bw_stats", AID_NET_BW_STATS, },
- { "net_bw_acct", AID_NET_BW_ACCT, },
- { "readproc", AID_READPROC, },
- { "wakelock", AID_WAKELOCK, },
-
- { "everybody", AID_EVERYBODY, },
- { "misc", AID_MISC, },
- { "nobody", AID_NOBODY, },
-};
-
-#define android_id_count \
- (sizeof(android_ids) / sizeof(android_ids[0]))
+#if !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
struct fs_path_config {
unsigned mode;
diff --git a/include/system/radio.h b/include/system/radio.h
index d73d3ae..03b252e 100644
--- a/include/system/radio.h
+++ b/include/system/radio.h
@@ -84,7 +84,7 @@
typedef unsigned int radio_handle_t;
/* Opaque meta data structure used by radio meta data API (see system/radio_metadata.h) */
-typedef struct radio_medtadata radio_metadata_t;
+typedef struct radio_metadata radio_metadata_t;
/* Additional attributes for an FM band configuration */
@@ -170,7 +170,8 @@
bool stereo; /* program is stereo or not */
bool digital; /* digital program or not (e.g HD Radio program) */
unsigned int signal_strength; /* signal strength from 0 to 100 */
- radio_metadata_t *metadata; /* non null if meta data are present (e.g PTY, song title ...) */
+ /* meta data (e.g PTY, song title ...), must not be NULL */
+ __attribute__((aligned(8))) radio_metadata_t *metadata;
} radio_program_info_t;
diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h
index 42de401..f93ad6e 100644
--- a/include/utils/KeyedVector.h
+++ b/include/utils/KeyedVector.h
@@ -21,7 +21,7 @@
#include <stdint.h>
#include <sys/types.h>
-#include <android/log.h>
+#include <log/log.h>
#include <utils/Errors.h>
#include <utils/SortedVector.h>
#include <utils/TypeHelpers.h>
diff --git a/init/Android.mk b/init/Android.mk
index 2122880..35e6f4f 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -93,7 +93,7 @@
libsquashfs_utils \
liblogwrap \
libcutils \
- libext4_utils_static \
+ libext4_utils \
libbase \
libc \
libselinux \
@@ -103,7 +103,7 @@
libcrypto \
libc++_static \
libdl \
- libsparse_static \
+ libsparse \
libz \
libprocessgroup \
libnl \
diff --git a/init/README.md b/init/README.md
new file mode 100644
index 0000000..cef0dbc
--- /dev/null
+++ b/init/README.md
@@ -0,0 +1,560 @@
+Android Init Language
+---------------------
+
+The Android Init Language consists of five broad classes of statements:
+Actions, Commands, Services, Options, and Imports.
+
+All of these are line-oriented, consisting of tokens separated by
+whitespace. The c-style backslash escapes may be used to insert
+whitespace into a token. Double quotes may also be used to prevent
+whitespace from breaking text into multiple tokens. The backslash,
+when it is the last character on a line, may be used for line-folding.
+
+Lines which start with a # (leading whitespace allowed) are comments.
+
+Actions and Services implicitly declare a new section. All commands
+or options belong to the section most recently declared. Commands
+or options before the first section are ignored.
+
+Actions and Services have unique names. If a second Action is defined
+with the same name as an existing one, its commands are appended to
+the commands of the existing action. If a second Service is defined
+with the same name as an existing one, it is ignored and an error
+message is logged.
+
+
+Init .rc Files
+--------------
+The init language is used in plain text files that take the .rc file
+extension. These are typically multiple of these in multiple
+locations on the system, described below.
+
+/init.rc is the primary .rc file and is loaded by the init executable
+at the beginning of its execution. It is responsible for the initial
+set up of the system. It imports /init.${ro.hardware}.rc which is the
+primary vendor supplied .rc file.
+
+During the mount\_all command, the init executable loads all of the
+files contained within the /{system,vendor,odm}/etc/init/ directories.
+These directories are intended for all Actions and Services used after
+file system mounting.
+
+One may specify paths in the mount\_all command line to have it import
+.rc files at the specified paths instead of the default ones listed above.
+This is primarily for supporting factory mode and other non-standard boot
+modes. The three default paths should be used for the normal boot process.
+
+The intention of these directories is:
+
+ 1. /system/etc/init/ is for core system items such as
+ SurfaceFlinger, MediaService, and logcatd.
+ 2. /vendor/etc/init/ is for SoC vendor items such as actions or
+ daemons needed for core SoC functionality.
+ 3. /odm/etc/init/ is for device manufacturer items such as
+ actions or daemons needed for motion sensor or other peripheral
+ functionality.
+
+All services whose binaries reside on the system, vendor, or odm
+partitions should have their service entries placed into a
+corresponding init .rc file, located in the /etc/init/
+directory of the partition where they reside. There is a build
+system macro, LOCAL\_INIT\_RC, that handles this for developers. Each
+init .rc file should additionally contain any actions associated with
+its service.
+
+An example is the logcatd.rc and Android.mk files located in the
+system/core/logcat directory. The LOCAL\_INIT\_RC macro in the
+Android.mk file places logcatd.rc in /system/etc/init/ during the
+build process. Init loads logcatd.rc during the mount\_all command and
+allows the service to be run and the action to be queued when
+appropriate.
+
+This break up of init .rc files according to their daemon is preferred
+to the previously used monolithic init .rc files. This approach
+ensures that the only service entries that init reads and the only
+actions that init performs correspond to services whose binaries are in
+fact present on the file system, which was not the case with the
+monolithic init .rc files. This additionally will aid in merge
+conflict resolution when multiple services are added to the system, as
+each one will go into a separate file.
+
+There are two options "early" and "late" in mount\_all command
+which can be set after optional paths. With "--early" set, the
+init executable will skip mounting entries with "latemount" flag
+and triggering fs encryption state event. With "--late" set,
+init executable will only mount entries with "latemount" flag but skip
+importing rc files. By default, no option is set, and mount\_all will
+process all entries in the given fstab.
+
+Actions
+-------
+Actions are named sequences of commands. Actions have a trigger which
+is used to determine when the action should occur. When an event
+occurs which matches an action's trigger, that action is added to
+the tail of a to-be-executed queue (unless it is already on the
+queue).
+
+Each action in the queue is dequeued in sequence and each command in
+that action is executed in sequence. Init handles other activities
+(device creation/destruction, property setting, process restarting)
+"between" the execution of the commands in activities.
+
+Actions take the form of:
+
+ on <trigger> [&& <trigger>]*
+ <command>
+ <command>
+ <command>
+
+
+Services
+--------
+Services are programs which init launches and (optionally) restarts
+when they exit. Services take the form of:
+
+ service <name> <pathname> [ <argument> ]*
+ <option>
+ <option>
+ ...
+
+
+Options
+-------
+Options are modifiers to services. They affect how and when init
+runs the service.
+
+`console [<console>]`
+> This service needs a console. The optional second parameter chooses a
+ specific console instead of the default. The default "/dev/console" can
+ be changed by setting the "androidboot.console" kernel parameter. In
+ all cases the leading "/dev/" should be omitted, so "/dev/tty0" would be
+ specified as just "console tty0".
+
+`critical`
+> This is a device-critical service. If it exits more than four times in
+ four minutes, the device will reboot into recovery mode.
+
+`disabled`
+> This service will not automatically start with its class.
+ It must be explicitly started by name.
+
+`setenv <name> <value>`
+> Set the environment variable _name_ to _value_ in the launched process.
+
+`socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]`
+> Create a unix domain socket named /dev/socket/_name_ and pass its fd to the
+ launched process. _type_ must be "dgram", "stream" or "seqpacket". User and
+ group default to 0. 'seclabel' is the SELinux security context for the
+ socket. It defaults to the service security context, as specified by
+ seclabel or computed based on the service executable file security context.
+ For native executables see libcutils android\_get\_control\_socket().
+
+`file <path> <type>`
+> Open a file path and pass its fd to the launched process. _type_ must be
+ "r", "w" or "rw". For native executables see libcutils
+ android\_get\_control\_file().
+
+`user <username>`
+> Change to 'username' before exec'ing this service.
+ Currently defaults to root. (??? probably should default to nobody)
+ As of Android M, processes should use this option even if they
+ require Linux capabilities. Previously, to acquire Linux
+ capabilities, a process would need to run as root, request the
+ capabilities, then drop to its desired uid. There is a new
+ mechanism through fs\_config that allows device manufacturers to add
+ Linux capabilities to specific binaries on a file system that should
+ be used instead. This mechanism is described on
+ <http://source.android.com/devices/tech/config/filesystem.html>. When
+ using this new mechanism, processes can use the user option to
+ select their desired uid without ever running as root.
+ As of Android O, processes can also request capabilities directly in their .rc
+ files. See the "capabilities" option below.
+
+`group <groupname> [ <groupname>\* ]`
+> Change to 'groupname' before exec'ing this service. Additional
+ groupnames beyond the (required) first one are used to set the
+ supplemental groups of the process (via setgroups()).
+ Currently defaults to root. (??? probably should default to nobody)
+
+`capabilities <capability> [ <capability>\* ]`
+> Set capabilities when exec'ing this service. 'capability' should be a Linux
+ capability without the "CAP\_" prefix, like "NET\_ADMIN" or "SETPCAP". See
+ http://man7.org/linux/man-pages/man7/capabilities.7.html for a list of Linux
+ capabilities.
+
+`seclabel <seclabel>`
+> Change to 'seclabel' before exec'ing this service.
+ Primarily for use by services run from the rootfs, e.g. ueventd, adbd.
+ Services on the system partition can instead use policy-defined transitions
+ based on their file security context.
+ If not specified and no transition is defined in policy, defaults to the init context.
+
+`oneshot`
+> Do not restart the service when it exits.
+
+`class <name>`
+> Specify a class name for the service. All services in a
+ named class may be started or stopped together. A service
+ is in the class "default" if one is not specified via the
+ class option.
+
+`onrestart`
+> Execute a Command (see below) when service restarts.
+
+`writepid <file...>`
+> Write the child's pid to the given files when it forks. Meant for
+ cgroup/cpuset usage.
+
+`priority <priority>`
+> Scheduling priority of the service process. This value has to be in range
+ -20 to 19. Default priority is 0. Priority is set via setpriority().
+
+`namespace <pid|mnt>`
+> Enter a new PID or mount namespace when forking the service.
+
+`oom_score_adjust <value>`
+> Sets the child's /proc/self/oom\_score\_adj to the specified value,
+ which must range from -1000 to 1000.
+
+
+Triggers
+--------
+Triggers are strings which can be used to match certain kinds of
+events and used to cause an action to occur.
+
+Triggers are subdivided into event triggers and property triggers.
+
+Event triggers are strings triggered by the 'trigger' command or by
+the QueueEventTrigger() function within the init executable. These
+take the form of a simple string such as 'boot' or 'late-init'.
+
+Property triggers are strings triggered when a named property changes
+value to a given new value or when a named property changes value to
+any new value. These take the form of 'property:<name>=<value>' and
+'property:<name>=\*' respectively. Property triggers are additionally
+evaluated and triggered accordingly during the initial boot phase of
+init.
+
+An Action can have multiple property triggers but may only have one
+event trigger.
+
+For example:
+`on boot && property:a=b` defines an action that is only executed when
+the 'boot' event trigger happens and the property a equals b.
+
+`on property:a=b && property:c=d` defines an action that is executed
+at three times:
+
+ 1. During initial boot if property a=b and property c=d.
+ 2. Any time that property a transitions to value b, while property c already equals d.
+ 3. Any time that property c transitions to value d, while property a already equals b.
+
+
+Commands
+--------
+
+`bootchart [start|stop]`
+> Start/stop bootcharting. These are present in the default init.rc files,
+ but bootcharting is only active if the file /data/bootchart/enabled exists;
+ otherwise bootchart start/stop are no-ops.
+
+`chmod <octal-mode> <path>`
+> Change file access permissions.
+
+`chown <owner> <group> <path>`
+> Change file owner and group.
+
+`class_start <serviceclass>`
+> Start all services of the specified class if they are
+ not already running.
+
+`class_stop <serviceclass>`
+> 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.
+
+`enable <servicename>`
+> Turns a disabled service into an enabled one as if the service did not
+ specify disabled.
+ If the service is supposed to be running, it will be started now.
+ Typically used when the bootloader sets a variable that indicates a specific
+ service should be started when needed. E.g.
+
+ 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. _seclabel_ can be a - to denote default.
+
+`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.
+
+`insmod [-f] <path> [<options>]`
+> Install the module at _path_ with the specified options.
+ -f: force installation of the module even if the version of the running kernel
+ and the version of the kernel for which the module was compiled do not match.
+
+`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_.
+
+`mkdir <path> [mode] [owner] [group]`
+> Create a directory at _path_, optionally with the given mode, owner, and
+ group. If not provided, the directory is created with permissions 755 and
+ 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> [ <path> ]\* [--<option>]`
+> Calls fs\_mgr\_mount\_all on the given fs\_mgr-format fstab and imports .rc files
+ at the specified paths (e.g., on the partitions just mounted) with optional
+ options "early" and "late".
+ Refer to the section of "Init .rc Files" for detail.
+
+`mount <type> <device> <dir> [ <flag>\* ] [<options>]`
+> Attempt to mount the named device at the directory _dir_
+ _flag_s include "ro", "rw", "remount", "noatime", ...
+ _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.
+ Not required for directories created by the init.rc as these are
+ automatically labeled correctly by init.
+
+`restorecon_recursive <path> [ <path>\* ]`
+> Recursively restore the directory tree named by _path_ to the
+ security contexts specified in the file\_contexts configuration.
+
+`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
+ within _value_.
+
+`setrlimit <resource> <cur> <max>`
+> Set the rlimit for a resource.
+
+`start <service>`
+> Start a service running if it is not already running.
+
+`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_
+
+`sysclktz <mins_west_of_gmt>`
+> Set the system clock base (0 if system clock ticks in GMT)
+
+`trigger <event>`
+> Trigger an event. Used to queue an action from another
+ action.
+
+`umount <path>`
+> Unmount the filesystem mounted at that path.
+
+`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.
+
+`write <path> <content>`
+> Open the file at _path_ and write a string to it with write(2).
+ If the file does not exist, it will be created. If it does exist,
+ it will be truncated. Properties are expanded within _content_.
+
+
+Imports
+-------
+The import keyword is not a command, but rather its own section and is
+handled immediately after the .rc file that contains it has finished
+being parsed. It takes the below form:
+
+`import <path>`
+> Parse an init config file, extending the current configuration.
+ If _path_ is a directory, each file in the directory is parsed as
+ a config file. It is not recursive, nested directories will
+ not be parsed.
+
+There are only two times where the init executable imports .rc files:
+
+ 1. When it imports /init.rc during initial boot
+ 2. When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
+ paths during mount_all
+
+
+Properties
+----------
+Init provides information about the services that it is responsible
+for via the below properties.
+
+`init.svc.<name>`
+> State of a named service ("stopped", "stopping", "running", "restarting")
+
+
+Boot timing
+-----------
+Init records some boot timing information in system properties.
+
+`ro.boottime.init`
+> Time after boot in ns (via the CLOCK\_BOOTTIME clock) at which the first
+ stage of init started.
+
+`ro.boottime.init.selinux`
+> How long it took the first stage to initialize SELinux.
+
+`ro.boottime.init.cold_boot_wait`
+> How long init waited for ueventd's coldboot phase to end.
+
+`ro.boottime.<service-name>`
+> Time after boot in ns (via the CLOCK\_BOOTTIME clock) that the service was
+ first started.
+
+
+Bootcharting
+------------
+This version of init contains code to perform "bootcharting": generating log
+files that can be later processed by the tools provided by <http://www.bootchart.org/>.
+
+On the emulator, use the -bootchart _timeout_ option to boot with bootcharting
+activated for _timeout_ seconds.
+
+On a device:
+
+ adb shell 'touch /data/bootchart/enabled'
+
+Don't forget to delete this file when you're done collecting data!
+
+The log files are written to /data/bootchart/. A script is provided to
+retrieve them and create a bootchart.tgz file that can be used with the
+bootchart command-line utility:
+
+ sudo apt-get install pybootchartgui
+ # 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.
+
+
+Comparing two bootcharts
+------------------------
+A handy script named compare-bootcharts.py can be used to compare the
+start/end time of selected processes. The aforementioned grab-bootchart.sh
+will leave a bootchart tarball named bootchart.tgz at /tmp/android-bootchart.
+If two such barballs are preserved on the host machine under different
+directories, the script can list the timestamps differences. For example:
+
+Usage: system/core/init/compare-bootcharts.py _base-bootchart-dir_ _exp-bootchart-dir_
+
+ process: baseline experiment (delta) - Unit is ms (a jiffy is 10 ms on the system)
+ ------------------------------------
+ /init: 50 40 (-10)
+ /system/bin/surfaceflinger: 4320 4470 (+150)
+ /system/bin/bootanimation: 6980 6990 (+10)
+ zygote64: 10410 10640 (+230)
+ zygote: 10410 10640 (+230)
+ system_server: 15350 15150 (-200)
+ bootanimation ends at: 33790 31230 (-2560)
+
+
+Systrace
+--------
+Systrace (<http://developer.android.com/tools/help/systrace.html>) can be
+used for obtaining performance analysis reports during boot
+time on userdebug or eng builds.
+
+Here is an example of trace events of "wm" and "am" categories:
+
+ $ANDROID_BUILD_TOP/external/chromium-trace/systrace.py \
+ wm am --boot
+
+This command will cause the device to reboot. After the device is rebooted and
+the boot sequence has finished, the trace report is obtained from the device
+and written as trace.html on the host by hitting Ctrl+C.
+
+Limitation: recording trace events is started after persistent properties are loaded, so
+the trace events that are emitted before that are not recorded. Several
+services such as vold, surfaceflinger, and servicemanager are affected by this
+limitation since they are started before persistent properties are loaded.
+Zygote initialization and the processes that are forked from the zygote are not
+affected.
+
+
+Debugging init
+--------------
+By default, programs executed by init will drop stdout and stderr into
+/dev/null. To help with debugging, you can execute your program via the
+Android program logwrapper. This will redirect stdout/stderr into the
+Android logging system (accessed via logcat).
+
+For example
+service akmd /system/bin/logwrapper /sbin/akmd
+
+For quicker turnaround when working on init itself, use:
+
+ mm -j &&
+ m ramdisk-nodeps &&
+ m bootimage-nodeps &&
+ adb reboot bootloader &&
+ fastboot boot $ANDROID_PRODUCT_OUT/boot.img
+
+Alternatively, use the emulator:
+
+ emulator -partition-size 1024 \
+ -verbose -show-kernel -no-window
diff --git a/init/readme.txt b/init/readme.txt
deleted file mode 100644
index 530b392..0000000
--- a/init/readme.txt
+++ /dev/null
@@ -1,560 +0,0 @@
-Android Init Language
----------------------
-
-The Android Init Language consists of five broad classes of statements,
-which are Actions, Commands, Services, Options, and Imports.
-
-All of these are line-oriented, consisting of tokens separated by
-whitespace. The c-style backslash escapes may be used to insert
-whitespace into a token. Double quotes may also be used to prevent
-whitespace from breaking text into multiple tokens. The backslash,
-when it is the last character on a line, may be used for line-folding.
-
-Lines which start with a # (leading whitespace allowed) are comments.
-
-Actions and Services implicitly declare a new section. All commands
-or options belong to the section most recently declared. Commands
-or options before the first section are ignored.
-
-Actions and Services have unique names. If a second Action is defined
-with the same name as an existing one, its commands are appended to
-the commands of the existing action. If a second Service is defined
-with the same name as an existing one, it is ignored and an error
-message is logged.
-
-
-Init .rc Files
---------------
-The init language is used in plaintext files that take the .rc file
-extension. These are typically multiple of these in multiple
-locations on the system, described below.
-
-/init.rc is the primary .rc file and is loaded by the init executable
-at the beginning of its execution. It is responsible for the initial
-set up of the system. It imports /init.${ro.hardware}.rc which is the
-primary vendor supplied .rc file.
-
-During the mount_all command, the init executable loads all of the
-files contained within the /{system,vendor,odm}/etc/init/ directories.
-These directories are intended for all Actions and Services used after
-file system mounting.
-
-One may specify paths in the mount_all command line to have it import
-.rc files at the specified paths instead of the default ones listed above.
-This is primarily for supporting factory mode and other non-standard boot
-modes. The three default paths should be used for the normal boot process.
-
-The intention of these directories is as follows
- 1) /system/etc/init/ is for core system items such as
- SurfaceFlinger, MediaService, and logcatd.
- 2) /vendor/etc/init/ is for SoC vendor items such as actions or
- daemons needed for core SoC functionality.
- 3) /odm/etc/init/ is for device manufacturer items such as
- actions or daemons needed for motion sensor or other peripheral
- functionality.
-
-All services whose binaries reside on the system, vendor, or odm
-partitions should have their service entries placed into a
-corresponding init .rc file, located in the /etc/init/
-directory of the partition where they reside. There is a build
-system macro, LOCAL_INIT_RC, that handles this for developers. Each
-init .rc file should additionally contain any actions associated with
-its service.
-
-An example is the logcatd.rc and Android.mk files located in the
-system/core/logcat directory. The LOCAL_INIT_RC macro in the
-Android.mk file places logcatd.rc in /system/etc/init/ during the
-build process. Init loads logcatd.rc during the mount_all command and
-allows the service to be run and the action to be queued when
-appropriate.
-
-This break up of init .rc files according to their daemon is preferred
-to the previously used monolithic init .rc files. This approach
-ensures that the only service entries that init reads and the only
-actions that init performs correspond to services whose binaries are in
-fact present on the file system, which was not the case with the
-monolithic init .rc files. This additionally will aid in merge
-conflict resolution when multiple services are added to the system, as
-each one will go into a separate file.
-
-There are two options "early" and "late" in mount_all command
-which can be set after optional paths. With "--early" set, the
-init executable will skip mounting entries with "latemount" flag
-and triggering fs encryption state event. With "--late" set,
-init executable will only mount entries with "latemount" flag but skip
-importing rc files. By default, no option is set, and mount_all will
-mount_all will process all entries in the given fstab.
-
-Actions
--------
-Actions are named sequences of commands. Actions have a trigger which
-is used to determine when the action should occur. When an event
-occurs which matches an action's trigger, that action is added to
-the tail of a to-be-executed queue (unless it is already on the
-queue).
-
-Each action in the queue is dequeued in sequence and each command in
-that action is executed in sequence. Init handles other activities
-(device creation/destruction, property setting, process restarting)
-"between" the execution of the commands in activities.
-
-Actions take the form of:
-
-on <trigger> [&& <trigger>]*
- <command>
- <command>
- <command>
-
-
-Services
---------
-Services are programs which init launches and (optionally) restarts
-when they exit. Services take the form of:
-
-service <name> <pathname> [ <argument> ]*
- <option>
- <option>
- ...
-
-
-Options
--------
-Options are modifiers to services. They affect how and when init
-runs the service.
-
-console [<console>]
- This service needs a console. The optional second parameter chooses a
- specific console instead of the default. The default "/dev/console" can
- be changed by setting the "androidboot.console" kernel parameter. In
- all cases the leading "/dev/" should be omitted, so "/dev/tty0" would be
- specified as just "console tty0".
-
-critical
- This is a device-critical service. If it exits more than four times in
- four minutes, the device will reboot into recovery mode.
-
-disabled
- This service will not automatically start with its class.
- It must be explicitly started by name.
-
-setenv <name> <value>
- Set the environment variable <name> to <value> in the launched process.
-
-socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]
- Create a unix domain socket named /dev/socket/<name> and pass its fd to the
- launched process. <type> must be "dgram", "stream" or "seqpacket". User and
- group default to 0. 'seclabel' is the SELinux security context for the
- socket. It defaults to the service security context, as specified by
- seclabel or computed based on the service executable file security context.
- For native executables see libcutils android_get_control_socket().
-
-file <path> <type>
- Open a file path and pass its fd to the launched process. <type> must be
- "r", "w" or "rw". For native executables see libcutils
- android_get_control_file().
-
-user <username>
- Change to 'username' before exec'ing this service.
- Currently defaults to root. (??? probably should default to nobody)
- As of Android M, processes should use this option even if they
- require Linux capabilities. Previously, to acquire Linux
- capabilities, a process would need to run as root, request the
- capabilities, then drop to its desired uid. There is a new
- mechanism through fs_config that allows device manufacturers to add
- Linux capabilities to specific binaries on a file system that should
- be used instead. This mechanism is described on
- http://source.android.com/devices/tech/config/filesystem.html. When
- using this new mechanism, processes can use the user option to
- select their desired uid without ever running as root.
- As of Android O, processes can also request capabilities directly in their .rc
- files. See the "capabilities" option below.
-
-group <groupname> [ <groupname> ]*
- Change to 'groupname' before exec'ing this service. Additional
- groupnames beyond the (required) first one are used to set the
- supplemental groups of the process (via setgroups()).
- Currently defaults to root. (??? probably should default to nobody)
-
-capabilities <capability> [ <capability> ]*
- Set capabilities when exec'ing this service. 'capability' should be a Linux
- capability without the "CAP_" prefix, like "NET_ADMIN" or "SETPCAP". See
- http://man7.org/linux/man-pages/man7/capabilities.7.html for a list of Linux
- capabilities.
-
-seclabel <seclabel>
- Change to 'seclabel' before exec'ing this service.
- Primarily for use by services run from the rootfs, e.g. ueventd, adbd.
- Services on the system partition can instead use policy-defined transitions
- based on their file security context.
- If not specified and no transition is defined in policy, defaults to the init context.
-
-oneshot
- Do not restart the service when it exits.
-
-class <name>
- Specify a class name for the service. All services in a
- named class may be started or stopped together. A service
- is in the class "default" if one is not specified via the
- class option.
-
-onrestart
- Execute a Command (see below) when service restarts.
-
-writepid <file...>
- Write the child's pid to the given files when it forks. Meant for
- cgroup/cpuset usage.
-
-priority <priority>
- Scheduling priority of the service process. This value has to be in range
- -20 to 19. Default priority is 0. Priority is set via setpriority().
-
-namespace <pid|mnt>
- Enter a new PID or mount namespace when forking the service.
-
-oom_score_adjust <value>
- Sets the child's /proc/self/oom_score_adj to the specified value,
- which must range from -1000 to 1000.
-
-
-Triggers
---------
-Triggers are strings which can be used to match certain kinds of
-events and used to cause an action to occur.
-
-Triggers are subdivided into event triggers and property triggers.
-
-Event triggers are strings triggered by the 'trigger' command or by
-the QueueEventTrigger() function within the init executable. These
-take the form of a simple string such as 'boot' or 'late-init'.
-
-Property triggers are strings triggered when a named property changes
-value to a given new value or when a named property changes value to
-any new value. These take the form of 'property:<name>=<value>' and
-'property:<name>=*' respectively. Property triggers are additionally
-evaluated and triggered accordingly during the initial boot phase of
-init.
-
-An Action can have multiple property triggers but may only have one
-event trigger.
-
-For example:
-'on boot && property:a=b' defines an action that is only executed when
-the 'boot' event trigger happens and the property a equals b.
-
-'on property:a=b && property:c=d' defines an action that is executed
-at three times,
- 1) During initial boot if property a=b and property c=d
- 2) Any time that property a transitions to value b, while property
- c already equals d.
- 3) Any time that property c transitions to value d, while property
- a already equals b.
-
-
-Commands
---------
-
-bootchart [start|stop]
- Start/stop bootcharting. These are present in the default init.rc files,
- but bootcharting is only active if the file /data/bootchart/enabled exists;
- otherwise bootchart start/stop are no-ops.
-
-chmod <octal-mode> <path>
- Change file access permissions.
-
-chown <owner> <group> <path>
- Change file owner and group.
-
-class_start <serviceclass>
- Start all services of the specified class if they are
- not already running.
-
-class_stop <serviceclass>
- 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.
-
-enable <servicename>
- Turns a disabled service into an enabled one as if the service did not
- specify disabled.
- If the service is supposed to be running, it will be started now.
- Typically used when the bootloader sets a variable that indicates a specific
- service should be started when needed. E.g.
- 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. <seclabel> can be a - to denote default.
-
-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.
-
-insmod [-f] <path> [<options>]
- Install the module at <path> with the specified options.
- -f
- Force installation of the module even if the version of the running kernel
- and the version of the kernel for which the module was compiled do not match.
-
-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>.
-
-mkdir <path> [mode] [owner] [group]
- Create a directory at <path>, optionally with the given mode, owner, and
- group. If not provided, the directory is created with permissions 755 and
- 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> [ <path> ]* [--<option>]
- Calls fs_mgr_mount_all on the given fs_mgr-format fstab and imports .rc files
- at the specified paths (e.g., on the partitions just mounted) with optional
- options "early" and "late".
- Refer to the section of "Init .rc Files" for detail.
-
-mount <type> <device> <dir> [ <flag> ]* [<options>]
- Attempt to mount the named device at the directory <dir>
- <flag>s include "ro", "rw", "remount", "noatime", ...
- <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.
- Not required for directories created by the init.rc as these are
- automatically labeled correctly by init.
-
-restorecon_recursive <path> [ <path> ]*
- Recursively restore the directory tree named by <path> to the
- security contexts specified in the file_contexts configuration.
-
-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
- within <value>.
-
-setrlimit <resource> <cur> <max>
- Set the rlimit for a resource.
-
-start <service>
- Start a service running if it is not already running.
-
-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>
-
-sysclktz <mins_west_of_gmt>
- Set the system clock base (0 if system clock ticks in GMT)
-
-trigger <event>
- Trigger an event. Used to queue an action from another
- action.
-
-umount <path>
- Unmount the filesystem mounted at that path.
-
-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.
-
-write <path> <content>
- Open the file at <path> and write a string to it with write(2).
- If the file does not exist, it will be created. If it does exist,
- it will be truncated. Properties are expanded within <content>.
-
-
-Imports
--------
-The import keyword is not a command, but rather its own section and is
-handled immediately after the .rc file that contains it has finished
-being parsed. It takes the below form:
-
-import <path>
- Parse an init config file, extending the current configuration.
- If <path> is a directory, each file in the directory is parsed as
- a config file. It is not recursive, nested directories will
- not be parsed.
-
-There are only two times where the init executable imports .rc files,
- 1) When it imports /init.rc during initial boot
- 2) When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
- paths during mount_all
-
-
-Properties
-----------
-Init provides information about the services that it is responsible
-for via the below properties.
-
-init.svc.<name>
- State of a named service ("stopped", "stopping", "running", "restarting")
-
-
-Boot timing
------------
-Init records some boot timing information in system properties.
-
-ro.boottime.init
- Time after boot in ns (via the CLOCK_BOOTTIME clock) at which the first
- stage of init started.
-
-ro.boottime.init.selinux
- How long it took the first stage to initialize SELinux.
-
-ro.boottime.init.cold_boot_wait
- How long init waited for ueventd's coldboot phase to end.
-
-ro.boottime.<service-name>
- Time after boot in ns (via the CLOCK_BOOTTIME clock) that the service was
- first started.
-
-
-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 -bootchart <timeout> option to boot with bootcharting
-activated for <timeout> seconds.
-
-On a device:
-
- adb shell 'touch /data/bootchart/enabled'
-
-Don't forget to delete this file when you're done collecting data!
-
-The log files are written to /data/bootchart/. A script is provided to
-retrieve them and create a bootchart.tgz file that can be used with the
-bootchart command-line utility:
-
- sudo apt-get install pybootchartgui
- # 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.
-
-
-Comparing two bootcharts
-------------------------
-A handy script named compare-bootcharts.py can be used to compare the
-start/end time of selected processes. The aforementioned grab-bootchart.sh
-will leave a bootchart tarball named bootchart.tgz at /tmp/android-bootchart.
-If two such barballs are preserved on the host machine under different
-directories, the script can list the timestamps differences. For example:
-
-Usage: system/core/init/compare-bootcharts.py base_bootchart_dir
- exp_bootchart_dir
-
-process: baseline experiment (delta)
- - Unit is ms (a jiffy is 10 ms on the system)
-------------------------------------
-/init: 50 40 (-10)
-/system/bin/surfaceflinger: 4320 4470 (+150)
-/system/bin/bootanimation: 6980 6990 (+10)
-zygote64: 10410 10640 (+230)
-zygote: 10410 10640 (+230)
-system_server: 15350 15150 (-200)
-bootanimation ends at: 33790 31230 (-2560)
-
-
-Systrace
---------
-Systrace [1] can be used for obtaining performance analysis reports during boot
-time on userdebug or eng builds.
-Here is an example of trace events of "wm" and "am" categories:
-
- $ANDROID_BUILD_TOP/external/chromium-trace/systrace.py wm am --boot
-
-This command will cause the device to reboot. After the device is rebooted and
-the boot sequence has finished, the trace report is obtained from the device
-and written as trace.html on the host by hitting Ctrl+C.
-
-LIMITATION
-Recording trace events is started after persistent properties are loaded, so
-the trace events that are emitted before that are not recorded. Several
-services such as vold, surfaceflinger, and servicemanager are affected by this
-limitation since they are started before persistent properties are loaded.
-Zygote initialization and the processes that are forked from the zygote are not
-affected.
-
-[1] http://developer.android.com/tools/help/systrace.html
-
-
-Debugging init
---------------
-By default, programs executed by init will drop stdout and stderr into
-/dev/null. To help with debugging, you can execute your program via the
-Android program logwrapper. This will redirect stdout/stderr into the
-Android logging system (accessed via logcat).
-
-For example
-service akmd /system/bin/logwrapper /sbin/akmd
-
-For quicker turnaround when working on init itself, use:
-
- mm -j &&
- m ramdisk-nodeps &&
- m bootimage-nodeps &&
- adb reboot bootloader &&
- fastboot boot $ANDROID_PRODUCT_OUT/boot.img
-
-Alternatively, use the emulator:
-
- emulator -partition-size 1024 -verbose -show-kernel -no-window
diff --git a/libbacktrace/BacktraceLog.h b/libbacktrace/BacktraceLog.h
index 0a27982..5c39f1c 100644
--- a/libbacktrace/BacktraceLog.h
+++ b/libbacktrace/BacktraceLog.h
@@ -19,7 +19,7 @@
#define LOG_TAG "libbacktrace"
-#include <android/log.h>
+#include <log/log.h>
// Macro to log the function name along with the warning message.
#define BACK_LOGW(format, ...) \
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index 4496375..0e31495 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -22,7 +22,8 @@
#include <sys/types.h>
#include <unistd.h>
-#include <android/log.h>
+#include <log/log.h>
+
#include <backtrace/backtrace_constants.h>
#include <backtrace/BacktraceMap.h>
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 39f8aba..b96e3ae 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -78,7 +78,6 @@
srcs: libcutils_nonwindows_sources + [
"android_reboot.c",
"ashmem-dev.c",
- "debugger.c",
"klog.cpp",
"partition_utils.c",
"properties.c",
@@ -86,9 +85,6 @@
"trace-dev.c",
"uevent.c",
],
-
- static_libs: ["libdebuggerd_client"],
- export_static_lib_headers: ["libdebuggerd_client"],
},
android_arm: {
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c
index db4ed11..92717c0 100644
--- a/libcutils/ashmem-dev.c
+++ b/libcutils/ashmem-dev.c
@@ -31,8 +31,8 @@
#include <sys/types.h>
#include <unistd.h>
-#include <android/log.h>
#include <cutils/ashmem.h>
+#include <log/log.h>
#define ASHMEM_DEVICE "/dev/ashmem"
diff --git a/libcutils/debugger.c b/libcutils/debugger.c
deleted file mode 100644
index c6bdd1a..0000000
--- a/libcutils/debugger.c
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <android/log.h>
-#include <cutils/debugger.h>
-#include <cutils/sockets.h>
-
-static int send_request(int sock_fd, void* msg_ptr, size_t msg_len) {
- int result = 0;
- if (TEMP_FAILURE_RETRY(write(sock_fd, msg_ptr, msg_len)) != (ssize_t) msg_len) {
- result = -1;
- } else {
- char ack;
- if (TEMP_FAILURE_RETRY(read(sock_fd, &ack, 1)) != 1) {
- result = -1;
- }
- }
- return result;
-}
-
-static int make_dump_request(debugger_action_t action, pid_t tid, int timeout_secs) {
- debugger_msg_t msg;
- memset(&msg, 0, sizeof(msg));
- msg.tid = tid;
- msg.action = action;
-
- int sock_fd = socket_local_client(DEBUGGER_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
- SOCK_STREAM | SOCK_CLOEXEC);
- if (sock_fd < 0) {
- return -1;
- }
-
- if (timeout_secs > 0) {
- struct timeval tm;
- tm.tv_sec = timeout_secs;
- tm.tv_usec = 0;
- if (setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tm, sizeof(tm)) == -1) {
- ALOGE("WARNING: Cannot set receive timeout value on socket: %s", strerror(errno));
- }
-
- if (setsockopt(sock_fd, SOL_SOCKET, SO_SNDTIMEO, &tm, sizeof(tm)) == -1) {
- ALOGE("WARNING: Cannot set send timeout value on socket: %s", strerror(errno));
- }
- }
-
- if (send_request(sock_fd, &msg, sizeof(msg)) < 0) {
- close(sock_fd);
- return -1;
- }
-
- return sock_fd;
-}
-
-int dump_backtrace_to_file(pid_t tid, int fd) {
- return dump_backtrace_to_file_timeout(tid, fd, 0);
-}
-
-int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs) {
- int sock_fd = make_dump_request(DEBUGGER_ACTION_DUMP_BACKTRACE, tid, timeout_secs);
- if (sock_fd < 0) {
- return -1;
- }
-
- /* Write the data read from the socket to the fd. */
- int result = 0;
- char buffer[1024];
- ssize_t n;
- while ((n = TEMP_FAILURE_RETRY(read(sock_fd, buffer, sizeof(buffer)))) > 0) {
- if (TEMP_FAILURE_RETRY(write(fd, buffer, n)) != n) {
- result = -1;
- break;
- }
- }
- close(sock_fd);
- return result;
-}
-
-int dump_tombstone(pid_t tid, char* pathbuf, size_t pathlen) {
- return dump_tombstone_timeout(tid, pathbuf, pathlen, 0);
-}
-
-int dump_tombstone_timeout(pid_t tid, char* pathbuf, size_t pathlen, int timeout_secs) {
- int sock_fd = make_dump_request(DEBUGGER_ACTION_DUMP_TOMBSTONE, tid, timeout_secs);
- if (sock_fd < 0) {
- return -1;
- }
-
- /* Read the tombstone file name. */
- char buffer[100]; /* This is larger than the largest tombstone path. */
- int result = 0;
- ssize_t n = TEMP_FAILURE_RETRY(read(sock_fd, buffer, sizeof(buffer) - 1));
- if (n <= 0) {
- result = -1;
- } else {
- if (pathbuf && pathlen) {
- if (n >= (ssize_t) pathlen) {
- n = pathlen - 1;
- }
- buffer[n] = '\0';
- memcpy(pathbuf, buffer, n + 1);
- }
- }
- close(sock_fd);
- return result;
-}
diff --git a/libcutils/dlmalloc_stubs.c b/libcutils/dlmalloc_stubs.c
index 6c07bed..2cff9dd 100644
--- a/libcutils/dlmalloc_stubs.c
+++ b/libcutils/dlmalloc_stubs.c
@@ -16,7 +16,7 @@
#define LOG_TAG "dlmalloc-stubs"
-#include "android/log.h"
+#include "log/log.h"
#define UNUSED __attribute__((__unused__))
diff --git a/libcutils/fs.c b/libcutils/fs.c
index c49233e..488fdfc 100644
--- a/libcutils/fs.c
+++ b/libcutils/fs.c
@@ -32,8 +32,8 @@
#include <sys/types.h>
#include <unistd.h>
-#include <android/log.h>
#include <cutils/fs.h>
+#include <log/log.h>
#define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
#define BUF_SIZE 64
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 6155d16..594b23d 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -33,7 +33,7 @@
#include <sys/stat.h>
#include <sys/types.h>
-#include <android/log.h>
+#include <log/log.h>
#include <private/android_filesystem_config.h>
#include <utils/Compat.h>
@@ -163,6 +163,10 @@
CAP_MASK_LONG(CAP_NET_RAW),
"system/bin/hw/android.hardware.wifi@1.0-service" },
+ /* Support Bluetooth legacy hal accessing /sys/class/rfkill */
+ { 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN),
+ "system/bin/hw/android.hardware.bluetooth@1.0-service" },
+
/* A non-privileged zygote that spawns isolated processes for web rendering. */
{ 0750, AID_ROOT, AID_ROOT, CAP_MASK_LONG(CAP_SETUID) |
CAP_MASK_LONG(CAP_SETGID) |
@@ -173,6 +177,9 @@
CAP_MASK_LONG(CAP_SETPCAP),
"system/bin/webview_zygote64" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump32" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump64" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/debuggerd" },
{ 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/*" },
diff --git a/libcutils/klog.cpp b/libcutils/klog.cpp
index 4bad28a..15adf6b 100644
--- a/libcutils/klog.cpp
+++ b/libcutils/klog.cpp
@@ -27,7 +27,7 @@
#include <cutils/android_get_control_file.h>
#include <cutils/klog.h>
-static int klog_level = KLOG_DEFAULT_LEVEL;
+static int klog_level = KLOG_INFO_LEVEL;
int klog_get_level(void) {
return klog_level;
diff --git a/libcutils/multiuser.c b/libcutils/multiuser.c
index 0ef337d..08d4d6c 100644
--- a/libcutils/multiuser.c
+++ b/libcutils/multiuser.c
@@ -37,6 +37,14 @@
}
}
+gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id) {
+ if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+ return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_GID_START);
+ } else {
+ return -1;
+ }
+}
+
gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id) {
if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_SHARED_GID_START);
diff --git a/libcutils/properties.c b/libcutils/properties.c
index 740c7a9..bdbddd0 100644
--- a/libcutils/properties.c
+++ b/libcutils/properties.c
@@ -26,9 +26,9 @@
#include <string.h>
#include <unistd.h>
-#include <android/log.h>
#include <cutils/properties.h>
#include <cutils/sockets.h>
+#include <log/log.h>
int8_t property_get_bool(const char *key, int8_t default_value) {
if (!key) {
diff --git a/libcutils/qtaguid.c b/libcutils/qtaguid.c
index ae5a503..22b8325 100644
--- a/libcutils/qtaguid.c
+++ b/libcutils/qtaguid.c
@@ -26,7 +26,7 @@
#include <string.h>
#include <unistd.h>
-#include <android/log.h>
+#include <log/log.h>
#include <cutils/qtaguid.h>
static const char* CTRL_PROCPATH = "/proc/net/xt_qtaguid/ctrl";
diff --git a/libcutils/sockets_unix.cpp b/libcutils/sockets_unix.cpp
index 5a14a5c..e91f358 100644
--- a/libcutils/sockets_unix.cpp
+++ b/libcutils/sockets_unix.cpp
@@ -25,9 +25,9 @@
#include <time.h>
#include <unistd.h>
-#include <android/log.h>
#include <cutils/android_get_control_file.h>
#include <cutils/sockets.h>
+#include <log/log.h>
#include "android_get_control_env.h"
diff --git a/libcutils/str_parms.c b/libcutils/str_parms.c
index 6bb7e58..8dafded 100644
--- a/libcutils/str_parms.c
+++ b/libcutils/str_parms.c
@@ -24,10 +24,10 @@
#include <stdlib.h>
#include <string.h>
-#include <android/log.h>
#include <cutils/hashmap.h>
#include <cutils/memory.h>
#include <cutils/str_parms.h>
+#include <log/log.h>
#define UNUSED __attribute__((unused))
diff --git a/libcutils/tests/multiuser_test.cpp b/libcutils/tests/multiuser_test.cpp
index 2307ea8..c5f58b4 100644
--- a/libcutils/tests/multiuser_test.cpp
+++ b/libcutils/tests/multiuser_test.cpp
@@ -58,6 +58,14 @@
EXPECT_EQ(1020000, multiuser_get_cache_gid(10, 10000));
}
+TEST(MultiuserTest, TestExt) {
+ EXPECT_EQ(-1, multiuser_get_ext_gid(0, 0));
+ EXPECT_EQ(-1, multiuser_get_ext_gid(0, 1000));
+ EXPECT_EQ(30000, multiuser_get_ext_gid(0, 10000));
+ EXPECT_EQ(-1, multiuser_get_ext_gid(0, 50000));
+ EXPECT_EQ(1030000, multiuser_get_ext_gid(10, 10000));
+}
+
TEST(MultiuserTest, TestShared) {
EXPECT_EQ(-1, multiuser_get_shared_gid(0, 0));
EXPECT_EQ(-1, multiuser_get_shared_gid(0, 1000));
diff --git a/libdiskconfig/config_mbr.c b/libdiskconfig/config_mbr.c
index 1d3cd20..ace9bbf 100644
--- a/libdiskconfig/config_mbr.c
+++ b/libdiskconfig/config_mbr.c
@@ -22,8 +22,8 @@
#include <stdlib.h>
#include <string.h>
-#include <android/log.h>
#include <diskconfig/diskconfig.h>
+#include <log/log.h>
/* start and len are in LBA units */
static void
diff --git a/libdiskconfig/diskconfig.c b/libdiskconfig/diskconfig.c
index 2d59ad9..c7e1b43 100644
--- a/libdiskconfig/diskconfig.c
+++ b/libdiskconfig/diskconfig.c
@@ -28,8 +28,8 @@
#include <sys/stat.h>
#include <unistd.h>
-#include <android/log.h>
#include <cutils/config_utils.h>
+#include <log/log.h>
#include <diskconfig/diskconfig.h>
diff --git a/libdiskconfig/diskutils.c b/libdiskconfig/diskutils.c
index 3a27601..fe1b4c1 100644
--- a/libdiskconfig/diskutils.c
+++ b/libdiskconfig/diskutils.c
@@ -26,7 +26,7 @@
#include <sys/stat.h>
#include <unistd.h>
-#include <android/log.h>
+#include <log/log.h>
#include <diskconfig/diskconfig.h>
diff --git a/libdiskconfig/dump_diskconfig.c b/libdiskconfig/dump_diskconfig.c
index c94e7f4..3c4f620 100644
--- a/libdiskconfig/dump_diskconfig.c
+++ b/libdiskconfig/dump_diskconfig.c
@@ -19,7 +19,7 @@
#include <stdio.h>
-#include <android/log.h>
+#include <log/log.h>
#include "diskconfig.h"
diff --git a/libdiskconfig/write_lst.c b/libdiskconfig/write_lst.c
index 21d4a31..c3d5c0a 100644
--- a/libdiskconfig/write_lst.c
+++ b/libdiskconfig/write_lst.c
@@ -23,8 +23,8 @@
#include <sys/types.h>
#include <unistd.h>
-#include <android/log.h>
#include <diskconfig/diskconfig.h>
+#include <log/log.h>
struct write_list *
alloc_wl(uint32_t data_len)
diff --git a/libion/ion.c b/libion/ion.c
index a7b22b8..9aaa6f2 100644
--- a/libion/ion.c
+++ b/libion/ion.c
@@ -29,8 +29,8 @@
#include <sys/types.h>
#include <unistd.h>
-#include <android/log.h>
#include <ion/ion.h>
+#include <log/log.h>
int ion_open()
{
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 02feb97..ec0352e 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1831,7 +1831,7 @@
return;
}
- setuid(AID_SYSTEM); // only one that can read security buffer
+ EXPECT_EQ(0, setuid(AID_SYSTEM)); // only one that can read security buffer
pid_t pid = getpid();
diff --git a/libmemtrack/memtrack.c b/libmemtrack/memtrack.c
index 29cc92c..9ed9451 100644
--- a/libmemtrack/memtrack.c
+++ b/libmemtrack/memtrack.c
@@ -22,8 +22,8 @@
#include <malloc.h>
#include <string.h>
-#include <android/log.h>
#include <hardware/memtrack.h>
+#include <log/log.h>
#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
diff --git a/libmemunreachable/log.h b/libmemunreachable/log.h
index dd146b6..cdfbfd9 100644
--- a/libmemunreachable/log.h
+++ b/libmemunreachable/log.h
@@ -19,6 +19,6 @@
#define LOG_TAG "libmemunreachable"
-#include <android/log.h>
+#include <log/log.h>
#endif // LIBMEMUNREACHABLE_LOG_H_
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index eafc53d..83f35b1 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -28,7 +28,7 @@
#include <cstring>
-#include <android/log.h>
+#include <log/log.h>
namespace android {
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 15fe054..94c46fc 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -19,10 +19,10 @@
#include <dlfcn.h>
#ifdef __ANDROID__
-#include "dlext_namespaces.h"
#define LOG_TAG "libnativeloader"
-#include "android/log.h"
+#include "dlext_namespaces.h"
#include "cutils/properties.h"
+#include "log/log.h"
#endif
#include "nativebridge/native_bridge.h"
diff --git a/libnetutils/dhcpclient.c b/libnetutils/dhcpclient.c
index d17bdd3..11c116a 100644
--- a/libnetutils/dhcpclient.c
+++ b/libnetutils/dhcpclient.c
@@ -31,8 +31,8 @@
#include <time.h>
#include <unistd.h>
-#include <android/log.h>
#include <cutils/properties.h>
+#include <log/log.h>
#include <netutils/ifc.h>
#include "dhcpmsg.h"
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c
index 275327a..a098d59 100644
--- a/libnetutils/ifc_utils.c
+++ b/libnetutils/ifc_utils.c
@@ -38,8 +38,8 @@
#ifdef ANDROID
#define LOG_TAG "NetUtils"
-#include <android/log.h>
#include <cutils/properties.h>
+#include <log/log.h>
#else
#define ALOGD printf
#define ALOGW printf
diff --git a/libnetutils/packet.c b/libnetutils/packet.c
index 56168e8..e53a4c8 100644
--- a/libnetutils/packet.c
+++ b/libnetutils/packet.c
@@ -28,7 +28,7 @@
#ifdef ANDROID
#define LOG_TAG "DHCP"
-#include <android/log.h>
+#include <log/log.h>
#else
#include <stdio.h>
#define ALOGD printf
diff --git a/libpackagelistparser/packagelistparser.c b/libpackagelistparser/packagelistparser.c
index f74b8b4..3e1a3d1 100644
--- a/libpackagelistparser/packagelistparser.c
+++ b/libpackagelistparser/packagelistparser.c
@@ -27,7 +27,7 @@
#include <string.h>
#include <sys/limits.h>
-#include <android/log.h>
+#include <log/log.h>
#include <packagelistparser/packagelistparser.h>
#define CLOGE(fmt, ...) \
diff --git a/libpixelflinger/codeflinger/ARMAssembler.cpp b/libpixelflinger/codeflinger/ARMAssembler.cpp
index 36c1326..ac009a9 100644
--- a/libpixelflinger/codeflinger/ARMAssembler.cpp
+++ b/libpixelflinger/codeflinger/ARMAssembler.cpp
@@ -20,8 +20,8 @@
#include <stdio.h>
#include <stdlib.h>
-#include <android/log.h>
#include <cutils/properties.h>
+#include <log/log.h>
#include <private/pixelflinger/ggl_context.h>
#include "ARMAssembler.h"
diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp b/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp
index e212f1b..c96cf4b 100644
--- a/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp
+++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp
@@ -21,7 +21,7 @@
#include <stdlib.h>
#include <sys/types.h>
-#include <android/log.h>
+#include <log/log.h>
#include "ARMAssemblerInterface.h"
diff --git a/libpixelflinger/codeflinger/Arm64Assembler.cpp b/libpixelflinger/codeflinger/Arm64Assembler.cpp
index fb297ec..bff87bb 100644
--- a/libpixelflinger/codeflinger/Arm64Assembler.cpp
+++ b/libpixelflinger/codeflinger/Arm64Assembler.cpp
@@ -32,8 +32,8 @@
#include <stdlib.h>
#include <string.h>
-#include <android/log.h>
#include <cutils/properties.h>
+#include <log/log.h>
#include <private/pixelflinger/ggl_context.h>
#include "codeflinger/Arm64Assembler.h"
diff --git a/libpixelflinger/codeflinger/CodeCache.cpp b/libpixelflinger/codeflinger/CodeCache.cpp
index 37bd074..8516640 100644
--- a/libpixelflinger/codeflinger/CodeCache.cpp
+++ b/libpixelflinger/codeflinger/CodeCache.cpp
@@ -23,8 +23,8 @@
#include <sys/mman.h>
#include <unistd.h>
-#include <android/log.h>
#include <cutils/ashmem.h>
+#include <log/log.h>
#include "CodeCache.h"
diff --git a/libpixelflinger/codeflinger/GGLAssembler.cpp b/libpixelflinger/codeflinger/GGLAssembler.cpp
index 0b9b5a4..91fbd53 100644
--- a/libpixelflinger/codeflinger/GGLAssembler.cpp
+++ b/libpixelflinger/codeflinger/GGLAssembler.cpp
@@ -23,7 +23,7 @@
#include <stdlib.h>
#include <sys/types.h>
-#include <android/log.h>
+#include <log/log.h>
#include "GGLAssembler.h"
diff --git a/libpixelflinger/codeflinger/MIPS64Assembler.cpp b/libpixelflinger/codeflinger/MIPS64Assembler.cpp
index a7bbaf7..d5e4cea 100644
--- a/libpixelflinger/codeflinger/MIPS64Assembler.cpp
+++ b/libpixelflinger/codeflinger/MIPS64Assembler.cpp
@@ -30,8 +30,8 @@
#include <stdio.h>
#include <stdlib.h>
-#include <android/log.h>
#include <cutils/properties.h>
+#include <log/log.h>
#include <private/pixelflinger/ggl_context.h>
#include "MIPS64Assembler.h"
diff --git a/libpixelflinger/codeflinger/MIPSAssembler.cpp b/libpixelflinger/codeflinger/MIPSAssembler.cpp
index 4cddcc8..865a568 100644
--- a/libpixelflinger/codeflinger/MIPSAssembler.cpp
+++ b/libpixelflinger/codeflinger/MIPSAssembler.cpp
@@ -47,14 +47,13 @@
** functions in ARMAssemblerInterface.cpp so they could be used as static initializers).
*/
-
#define LOG_TAG "MIPSAssembler"
#include <stdio.h>
#include <stdlib.h>
-#include <android/log.h>
#include <cutils/properties.h>
+#include <log/log.h>
#include <private/pixelflinger/ggl_context.h>
#include "CodeCache.h"
diff --git a/libpixelflinger/codeflinger/blending.cpp b/libpixelflinger/codeflinger/blending.cpp
index 092f140..a55dfe3 100644
--- a/libpixelflinger/codeflinger/blending.cpp
+++ b/libpixelflinger/codeflinger/blending.cpp
@@ -23,7 +23,7 @@
#include <stdlib.h>
#include <sys/types.h>
-#include <android/log.h>
+#include <log/log.h>
#include "GGLAssembler.h"
diff --git a/libpixelflinger/codeflinger/load_store.cpp b/libpixelflinger/codeflinger/load_store.cpp
index b8a0e55..da21e1d 100644
--- a/libpixelflinger/codeflinger/load_store.cpp
+++ b/libpixelflinger/codeflinger/load_store.cpp
@@ -20,7 +20,7 @@
#include <assert.h>
#include <stdio.h>
-#include <android/log.h>
+#include <log/log.h>
#include "GGLAssembler.h"
diff --git a/libpixelflinger/codeflinger/texturing.cpp b/libpixelflinger/codeflinger/texturing.cpp
index f4f4657..4c357af 100644
--- a/libpixelflinger/codeflinger/texturing.cpp
+++ b/libpixelflinger/codeflinger/texturing.cpp
@@ -23,7 +23,7 @@
#include <stdlib.h>
#include <sys/types.h>
-#include <android/log.h>
+#include <log/log.h>
#include "GGLAssembler.h"
diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp
index 1a2f6fb..c6cf5bf 100644
--- a/libpixelflinger/scanline.cpp
+++ b/libpixelflinger/scanline.cpp
@@ -22,8 +22,8 @@
#include <stdlib.h>
#include <string.h>
-#include <android/log.h>
#include <cutils/memory.h>
+#include <log/log.h>
#include "buffer.h"
#include "scanline.h"
diff --git a/libpixelflinger/trap.cpp b/libpixelflinger/trap.cpp
index fa6338a..234bfdd 100644
--- a/libpixelflinger/trap.cpp
+++ b/libpixelflinger/trap.cpp
@@ -21,8 +21,8 @@
#include <stdio.h>
#include <stdlib.h>
-#include <android/log.h>
#include <cutils/memory.h>
+#include <log/log.h>
#include "trap.h"
#include "picker.h"
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 7a6ae8a..dd8b5fd 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -1,7 +1,9 @@
// Copyright 2010 The Android Open Source Project
-cc_defaults {
- name: "libsparse_defaults",
+cc_library {
+ name: "libsparse",
+ host_supported: true,
+ unique_host_soname: true,
srcs: [
"backed_block.c",
"output_file.c",
@@ -13,32 +15,19 @@
cflags: ["-Werror"],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
-}
-
-cc_library_host_static {
- name: "libsparse_host",
- defaults: ["libsparse_defaults"],
- static_libs: ["libz"],
target: {
+ host: {
+ shared_libs: ["libz-host"],
+ },
+ android: {
+ shared_libs: ["libz"],
+ },
windows: {
enabled: true,
},
},
}
-cc_library_shared {
- name: "libsparse",
- defaults: ["libsparse_defaults"],
- shared_libs: ["libz"],
-}
-
-cc_library_static {
- name: "libsparse_static",
- host_supported: true,
- defaults: ["libsparse_defaults"],
- static_libs: ["libz"],
-}
-
cc_binary {
name: "simg2img",
host_supported: true,
@@ -47,7 +36,7 @@
"sparse_crc32.c",
],
static_libs: [
- "libsparse_static",
+ "libsparse",
"libz",
],
@@ -59,7 +48,7 @@
host_supported: true,
srcs: ["img2simg.c"],
static_libs: [
- "libsparse_static",
+ "libsparse",
"libz",
],
@@ -70,7 +59,7 @@
name: "append2simg",
srcs: ["append2simg.c"],
static_libs: [
- "libsparse_static",
+ "libsparse",
"libz",
],
diff --git a/libsuspend/autosuspend.c b/libsuspend/autosuspend.c
index 64d1bfc..54730c2 100644
--- a/libsuspend/autosuspend.c
+++ b/libsuspend/autosuspend.c
@@ -18,7 +18,7 @@
#include <stdbool.h>
-#include <android/log.h>
+#include <log/log.h>
#include <suspend/autosuspend.h>
diff --git a/libsuspend/autosuspend_autosleep.c b/libsuspend/autosuspend_autosleep.c
index 97109ac..77d8db0 100644
--- a/libsuspend/autosuspend_autosleep.c
+++ b/libsuspend/autosuspend_autosleep.c
@@ -24,7 +24,7 @@
#include <sys/types.h>
#include <unistd.h>
-#include <android/log.h>
+#include <log/log.h>
#include "autosuspend_ops.h"
diff --git a/libsuspend/autosuspend_earlysuspend.c b/libsuspend/autosuspend_earlysuspend.c
index 9519e51..809ee82 100644
--- a/libsuspend/autosuspend_earlysuspend.c
+++ b/libsuspend/autosuspend_earlysuspend.c
@@ -26,7 +26,7 @@
#include <sys/types.h>
#include <unistd.h>
-#include <android/log.h>
+#include <log/log.h>
#include "autosuspend_ops.h"
diff --git a/libsuspend/autosuspend_wakeup_count.c b/libsuspend/autosuspend_wakeup_count.c
index 3457d5b..2da204a 100644
--- a/libsuspend/autosuspend_wakeup_count.c
+++ b/libsuspend/autosuspend_wakeup_count.c
@@ -29,7 +29,7 @@
#include <sys/types.h>
#include <unistd.h>
-#include <android/log.h>
+#include <log/log.h>
#include "autosuspend_ops.h"
diff --git a/libsync/include/sync/sync.h b/libsync/include/sync/sync.h
index 2e5d82f..50ed0ac 100644
--- a/libsync/include/sync/sync.h
+++ b/libsync/include/sync/sync.h
@@ -22,9 +22,16 @@
#include <sys/cdefs.h>
#include <stdint.h>
+#include <linux/types.h>
+
__BEGIN_DECLS
-// XXX: These structs are copied from the header "linux/sync.h".
+struct sync_legacy_merge_data {
+ int32_t fd2;
+ char name[32];
+ int32_t fence;
+};
+
struct sync_fence_info_data {
uint32_t len;
char name[32];
@@ -41,6 +48,108 @@
uint8_t driver_data[0];
};
+#define SYNC_IOC_MAGIC '>'
+
+/**
+ * DOC: SYNC_IOC_LEGACY_WAIT - wait for a fence to signal
+ *
+ * pass timeout in milliseconds. Waits indefinitely timeout < 0.
+ *
+ * This is the legacy version of the Sync API before the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_LEGACY_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32)
+
+/**
+ * DOC: SYNC_IOC_MERGE - merge two fences
+ *
+ * Takes a struct sync_merge_data. Creates a new fence containing copies of
+ * the sync_pts in both the calling fd and sync_merge_data.fd2. Returns the
+ * new fence's fd in sync_merge_data.fence
+ *
+ * This is the legacy version of the Sync API before the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_LEGACY_MERGE _IOWR(SYNC_IOC_MAGIC, 1, \
+ struct sync_legacy_merge_data)
+
+/**
+ * DOC: SYNC_IOC_LEGACY_FENCE_INFO - get detailed information on a fence
+ *
+ * Takes a struct sync_fence_info_data with extra space allocated for pt_info.
+ * Caller should write the size of the buffer into len. On return, len is
+ * updated to reflect the total size of the sync_fence_info_data including
+ * pt_info.
+ *
+ * pt_info is a buffer containing sync_pt_infos for every sync_pt in the fence.
+ * To iterate over the sync_pt_infos, use the sync_pt_info.len field.
+ *
+ * This is the legacy version of the Sync API before the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_LEGACY_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2,\
+ struct sync_fence_info_data)
+
+struct sync_merge_data {
+ char name[32];
+ int32_t fd2;
+ int32_t fence;
+ uint32_t flags;
+ uint32_t pad;
+};
+
+struct sync_file_info {
+ char name[32];
+ int32_t status;
+ uint32_t flags;
+ uint32_t num_fences;
+ uint32_t pad;
+
+ uint64_t sync_fence_info;
+};
+
+struct sync_fence_info {
+ char obj_name[32];
+ char driver_name[32];
+ int32_t status;
+ uint32_t flags;
+ uint64_t timestamp_ns;
+};
+
+/**
+ * Mainline API:
+ *
+ * Opcodes 0, 1 and 2 were burned during a API change to avoid users of the
+ * old API to get weird errors when trying to handling sync_files. The API
+ * change happened during the de-stage of the Sync Framework when there was
+ * no upstream users available.
+ */
+
+/**
+ * DOC: SYNC_IOC_MERGE - merge two fences
+ *
+ * Takes a struct sync_merge_data. Creates a new fence containing copies of
+ * the sync_pts in both the calling fd and sync_merge_data.fd2. Returns the
+ * new fence's fd in sync_merge_data.fence
+ *
+ * This is the new version of the Sync API after the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 3, struct sync_merge_data)
+
+/**
+ * DOC: SYNC_IOC_FILE_INFO - get detailed information on a sync_file
+ *
+ * Takes a struct sync_file_info. If num_fences is 0, the field is updated
+ * with the actual number of fences. If num_fences is > 0, the system will
+ * use the pointer provided on sync_fence_info to return up to num_fences of
+ * struct sync_fence_info, with detailed fence information.
+ *
+ * This is the new version of the Sync API after the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_FILE_INFO _IOWR(SYNC_IOC_MAGIC, 4, struct sync_file_info)
+
/* timeout in msecs */
int sync_wait(int fd, int timeout);
int sync_merge(const char *name, int fd1, int fd2);
diff --git a/libsync/sync.c b/libsync/sync.c
index 6281b20..9ed03db 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -20,6 +20,8 @@
#include <malloc.h>
#include <stdint.h>
#include <string.h>
+#include <errno.h>
+#include <poll.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
@@ -27,18 +29,6 @@
#include <sync/sync.h>
-// The sync code is undergoing a major change. Add enough in to get
-// everything to compile wih the latest uapi headers.
-struct sync_merge_data {
- int32_t fd2;
- char name[32];
- int32_t fence;
-};
-
-#define SYNC_IOC_MAGIC '>'
-#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32)
-#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 1, struct sync_merge_data)
-#define SYNC_IOC_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2, struct sync_fence_info_data)
struct sw_sync_create_fence_data {
__u32 value;
@@ -52,43 +42,133 @@
int sync_wait(int fd, int timeout)
{
- __s32 to = timeout;
+ struct pollfd fds;
+ int ret;
- return ioctl(fd, SYNC_IOC_WAIT, &to);
+ if (fd < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ fds.fd = fd;
+ fds.events = POLLIN;
+
+ do {
+ ret = poll(&fds, 1, timeout);
+ if (ret > 0) {
+ if (fds.revents & (POLLERR | POLLNVAL)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+ } else if (ret == 0) {
+ errno = ETIME;
+ return -1;
+ }
+ } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
+
+ return ret;
}
int sync_merge(const char *name, int fd1, int fd2)
{
+ struct sync_legacy_merge_data legacy_data;
struct sync_merge_data data;
- int err;
+ int ret;
data.fd2 = fd2;
strlcpy(data.name, name, sizeof(data.name));
+ data.flags = 0;
+ data.pad = 0;
- err = ioctl(fd1, SYNC_IOC_MERGE, &data);
- if (err < 0)
- return err;
+ ret = ioctl(fd1, SYNC_IOC_MERGE, &data);
+ if (ret < 0 && errno == ENOTTY) {
+ legacy_data.fd2 = fd2;
+ strlcpy(legacy_data.name, name, sizeof(legacy_data.name));
+
+ ret = ioctl(fd1, SYNC_IOC_LEGACY_MERGE, &legacy_data);
+ if (ret < 0)
+ return ret;
+
+ return legacy_data.fence;
+ } else if (ret < 0) {
+ return ret;
+ }
return data.fence;
}
struct sync_fence_info_data *sync_fence_info(int fd)
{
- struct sync_fence_info_data *info;
- int err;
+ struct sync_fence_info_data *legacy_info;
+ struct sync_pt_info *legacy_pt_info;
+ struct sync_file_info *info;
+ struct sync_fence_info *fence_info;
+ int err, num_fences, i;
- info = malloc(4096);
- if (info == NULL)
+ legacy_info = malloc(4096);
+ if (legacy_info == NULL)
return NULL;
- info->len = 4096;
- err = ioctl(fd, SYNC_IOC_FENCE_INFO, info);
- if (err < 0) {
- free(info);
+ legacy_info->len = 4096;
+ err = ioctl(fd, SYNC_IOC_LEGACY_FENCE_INFO, legacy_info);
+ if (err < 0 && errno != ENOTTY) {
+ free(legacy_info);
return NULL;
+ } else if (err == 0) {
+ return legacy_info;
}
- return info;
+ info = calloc(1, sizeof(*info));
+ if (info == NULL)
+ goto free;
+
+ err = ioctl(fd, SYNC_IOC_FILE_INFO, info);
+ if (err < 0)
+ goto free;
+
+ num_fences = info->num_fences;
+
+ if (num_fences) {
+ info->flags = 0;
+ info->num_fences = num_fences;
+ info->sync_fence_info = (uint64_t) calloc(num_fences,
+ sizeof(struct sync_fence_info));
+ if ((void *)info->sync_fence_info == NULL)
+ goto free;
+
+ err = ioctl(fd, SYNC_IOC_FILE_INFO, info);
+ if (err < 0) {
+ free((void *)info->sync_fence_info);
+ goto free;
+ }
+ }
+
+ legacy_info->len = sizeof(*legacy_info) +
+ num_fences * sizeof(struct sync_fence_info);
+ strlcpy(legacy_info->name, info->name, sizeof(legacy_info->name));
+ legacy_info->status = info->status;
+
+ legacy_pt_info = (struct sync_pt_info *)legacy_info->pt_info;
+ fence_info = (struct sync_fence_info *)info->sync_fence_info;
+ for (i = 0 ; i < num_fences ; i++) {
+ legacy_pt_info[i].len = sizeof(*legacy_pt_info);
+ strlcpy(legacy_pt_info[i].obj_name, fence_info[i].obj_name,
+ sizeof(legacy_pt_info->obj_name));
+ strlcpy(legacy_pt_info[i].driver_name, fence_info[i].driver_name,
+ sizeof(legacy_pt_info->driver_name));
+ legacy_pt_info[i].status = fence_info[i].status;
+ legacy_pt_info[i].timestamp_ns = fence_info[i].timestamp_ns;
+ }
+
+ free((void *)info->sync_fence_info);
+ free(info);
+ return legacy_info;
+
+free:
+ free(legacy_info);
+ free(info);
+ return NULL;
}
struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
@@ -113,7 +193,13 @@
int sw_sync_timeline_create(void)
{
- return open("/dev/sw_sync", O_RDWR);
+ int ret;
+
+ ret = open("/sys/kernel/debug/sync/sw_sync", O_RDWR);
+ if (ret < 0)
+ ret = open("/dev/sw_sync", O_RDWR);
+
+ return ret;
}
int sw_sync_timeline_inc(int fd, unsigned count)
diff --git a/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp
index 2c409dc..ff8a300 100644
--- a/libsync/tests/sync_test.cpp
+++ b/libsync/tests/sync_test.cpp
@@ -348,33 +348,6 @@
ASSERT_EQ(selfMergeFence.getSignaledCount(), 1);
}
-TEST(FenceTest, WaitOnDestroyedTimeline) {
- SyncTimeline timeline;
- ASSERT_TRUE(timeline.isValid());
-
- SyncFence fenceSig(timeline, 100);
- SyncFence fenceKill(timeline, 200);
-
- // Spawn a thread to wait on a fence when the timeline is killed.
- thread waitThread{
- [&]() {
- ASSERT_EQ(timeline.inc(100), 0);
-
- ASSERT_EQ(fenceKill.wait(-1), -1);
- ASSERT_EQ(errno, ENOENT);
- }
- };
-
- // Wait for the thread to spool up.
- fenceSig.wait();
-
- // Kill the timeline.
- timeline.destroy();
-
- // wait for the thread to clean up.
- waitThread.join();
-}
-
TEST(FenceTest, PollOnDestroyedTimeline) {
SyncTimeline timeline;
ASSERT_TRUE(timeline.isValid());
@@ -391,8 +364,7 @@
struct pollfd fds;
fds.fd = fenceKill.getFd();
fds.events = POLLIN | POLLERR;
- ASSERT_EQ(poll(&fds, 1, -1), 1);
- ASSERT_TRUE(fds.revents & POLLERR);
+ ASSERT_EQ(poll(&fds, 1, 0), 0);
}
};
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 26e27cf..77e69e4 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -21,7 +21,7 @@
#include <sys/eventfd.h>
#include <unistd.h>
-#include <android/log.h>
+#include <log/log.h>
#include <utils/Looper.h>
#include <utils/Timers.h>
diff --git a/libutils/SharedBuffer.cpp b/libutils/SharedBuffer.cpp
index 269326a..957aedb 100644
--- a/libutils/SharedBuffer.cpp
+++ b/libutils/SharedBuffer.cpp
@@ -19,7 +19,7 @@
#include <stdlib.h>
#include <string.h>
-#include <android/log.h>
+#include <log/log.h>
#include "SharedBuffer.h"
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index c32f462..f1a41b9 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -19,7 +19,7 @@
#include <limits.h>
#include <stddef.h>
-#include <android/log.h>
+#include <log/log.h>
#include <utils/Unicode.h>
#if defined(_WIN32)
diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
index 893e4f2..f7ca8f4 100644
--- a/libutils/VectorImpl.cpp
+++ b/libutils/VectorImpl.cpp
@@ -20,7 +20,7 @@
#include <stdlib.h>
#include <string.h>
-#include <android/log.h>
+#include <log/log.h>
#include <utils/Errors.h>
#include <utils/VectorImpl.h>
diff --git a/libziparchive/zip_archive_stream_entry.cc b/libziparchive/zip_archive_stream_entry.cc
index 64b24c3..3f336a6 100644
--- a/libziparchive/zip_archive_stream_entry.cc
+++ b/libziparchive/zip_archive_stream_entry.cc
@@ -26,8 +26,9 @@
#include <memory>
#include <vector>
-#include <android/log.h>
#include <android-base/file.h>
+#include <log/log.h>
+
#include <ziparchive/zip_archive.h>
#include <ziparchive/zip_archive_stream_entry.h>
#include <zlib.h>
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 49746b3..8a6168c 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -31,8 +31,8 @@
#include <sys/types.h>
#include <unistd.h>
-#include <android/log.h>
#include <cutils/sockets.h>
+#include <log/log.h>
#include <processgroup/processgroup.h>
#ifndef __unused
diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.c
index 1bfecd6..3ad0983 100644
--- a/logwrapper/logwrap.c
+++ b/logwrapper/logwrap.c
@@ -28,8 +28,8 @@
#include <sys/wait.h>
#include <unistd.h>
-#include <android/log.h>
#include <cutils/klog.h>
+#include <log/log.h>
#include <logwrap/logwrap.h>
#include <private/android_filesystem_config.h>
diff --git a/logwrapper/logwrapper.c b/logwrapper/logwrapper.c
index 28fe530..33454c6 100644
--- a/logwrapper/logwrapper.c
+++ b/logwrapper/logwrapper.c
@@ -20,8 +20,8 @@
#include <sys/wait.h>
#include <unistd.h>
-#include <android/log.h>
#include <cutils/klog.h>
+#include <log/log.h>
#include <logwrap/logwrap.h>
void fatal(const char *msg) {
diff --git a/rootdir/init.rc b/rootdir/init.rc
index c0a0fce..791d67f 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -312,6 +312,7 @@
# Make sure /sys/kernel/debug (if present) is labeled properly
# Note that tracefs may be mounted under debug, so we need to cross filesystems
restorecon --recursive --cross-filesystems /sys/kernel/debug
+ chmod 0755 /sys/kernel/debug/tracing
# We chown/chmod /cache again so because mount is run as root + defaults
chown system cache /cache
diff --git a/rootdir/init.usb.configfs.rc b/rootdir/init.usb.configfs.rc
index dc875b4..32f0198 100644
--- a/rootdir/init.usb.configfs.rc
+++ b/rootdir/init.usb.configfs.rc
@@ -70,7 +70,7 @@
on property:sys.usb.config=audio_source && property:sys.usb.configfs=1
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "audiosource"
- symlink /config/usb_gadget/g1/functions/audio_source.gs2 /config/usb_gadget/g1/configs/b.1/f1
+ symlink /config/usb_gadget/g1/functions/audio_source.gs3 /config/usb_gadget/g1/configs/b.1/f1
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
setprop sys.usb.state ${sys.usb.config}
@@ -79,7 +79,7 @@
on property:sys.usb.ffs.ready=1 && property:sys.usb.config=audio_source,adb && property:sys.usb.configfs=1
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "audiosource_adb"
- symlink /config/usb_gadget/g1/functions/audio_source.gs2 /config/usb_gadget/g1/configs/b.1/f1
+ symlink /config/usb_gadget/g1/functions/audio_source.gs3 /config/usb_gadget/g1/configs/b.1/f1
symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
setprop sys.usb.state ${sys.usb.config}
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 8725113..0633a68 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -38,8 +38,6 @@
# these should not be world writable
/dev/diag 0660 radio radio
/dev/diag_arm9 0660 radio radio
-/dev/android_adb 0660 adb adb
-/dev/android_adb_enable 0660 adb adb
/dev/ttyMSM0 0600 bluetooth bluetooth
/dev/uhid 0660 system bluetooth
/dev/uinput 0660 system bluetooth
diff --git a/trusty/gatekeeper/trusty_gatekeeper.cpp b/trusty/gatekeeper/trusty_gatekeeper.cpp
index 7e55fb1..b3fbfa9 100644
--- a/trusty/gatekeeper/trusty_gatekeeper.cpp
+++ b/trusty/gatekeeper/trusty_gatekeeper.cpp
@@ -19,9 +19,10 @@
#include <assert.h>
#include <errno.h>
#include <stdio.h>
+
#include <type_traits>
-#include <android/log.h>
+#include <log/log.h>
#include "trusty_gatekeeper.h"
#include "trusty_gatekeeper_ipc.h"
diff --git a/trusty/gatekeeper/trusty_gatekeeper_ipc.c b/trusty/gatekeeper/trusty_gatekeeper_ipc.c
index 45e65a7..f67944b 100644
--- a/trusty/gatekeeper/trusty_gatekeeper_ipc.c
+++ b/trusty/gatekeeper/trusty_gatekeeper_ipc.c
@@ -22,7 +22,7 @@
#include <string.h>
#include <unistd.h>
-#include <android/log.h>
+#include <log/log.h>
#include <trusty/tipc.h>
#include "trusty_gatekeeper_ipc.h"
diff --git a/trusty/keymaster/trusty_keymaster_device.cpp b/trusty/keymaster/trusty_keymaster_device.cpp
index de5e463..1368f88 100644
--- a/trusty/keymaster/trusty_keymaster_device.cpp
+++ b/trusty/keymaster/trusty_keymaster_device.cpp
@@ -27,9 +27,9 @@
#include <type_traits>
-#include <android/log.h>
#include <hardware/keymaster0.h>
#include <keymaster/authorization_set.h>
+#include <log/log.h>
#include "trusty_keymaster_device.h"
#include "trusty_keymaster_ipc.h"
diff --git a/trusty/keymaster/trusty_keymaster_ipc.c b/trusty/keymaster/trusty_keymaster_ipc.c
index 8755093..88546af 100644
--- a/trusty/keymaster/trusty_keymaster_ipc.c
+++ b/trusty/keymaster/trusty_keymaster_ipc.c
@@ -23,7 +23,7 @@
#include <string.h>
#include <unistd.h>
-#include <android/log.h>
+#include <log/log.h>
#include <trusty/tipc.h>
#include "trusty_keymaster_ipc.h"
diff --git a/trusty/libtrusty/trusty.c b/trusty/libtrusty/trusty.c
index 2398a53..a6238af 100644
--- a/trusty/libtrusty/trusty.c
+++ b/trusty/libtrusty/trusty.c
@@ -25,7 +25,7 @@
#include <sys/ioctl.h>
#include <unistd.h>
-#include <android/log.h>
+#include <log/log.h>
#include "tipc_ioctl.h"
diff --git a/trusty/nvram/trusty_nvram_implementation.cpp b/trusty/nvram/trusty_nvram_implementation.cpp
index ddaf333..9215c85 100644
--- a/trusty/nvram/trusty_nvram_implementation.cpp
+++ b/trusty/nvram/trusty_nvram_implementation.cpp
@@ -22,8 +22,8 @@
#include <string.h>
#include <unistd.h>
-#include <android/log.h>
#include <hardware/nvram.h>
+#include <log/log.h>
#include <trusty/tipc.h>
#include <nvram/messages/blob.h>
diff --git a/trusty/storage/lib/storage.c b/trusty/storage/lib/storage.c
index 3002c0b..915bc17 100644
--- a/trusty/storage/lib/storage.c
+++ b/trusty/storage/lib/storage.c
@@ -23,7 +23,7 @@
#include <string.h>
#include <sys/uio.h>
-#include <android/log.h>
+#include <log/log.h>
#include <trusty/tipc.h>
#include <trusty/lib/storage.h>
diff --git a/trusty/storage/proxy/log.h b/trusty/storage/proxy/log.h
index 3d2e654..c81beab 100644
--- a/trusty/storage/proxy/log.h
+++ b/trusty/storage/proxy/log.h
@@ -16,5 +16,5 @@
#define LOG_TAG "storageproxyd"
-#include <android/log.h>
+#include <log/log.h>
diff --git a/tzdatacheck/tzdatacheck.cpp b/tzdatacheck/tzdatacheck.cpp
index c1ab2ac..fb5c84b 100644
--- a/tzdatacheck/tzdatacheck.cpp
+++ b/tzdatacheck/tzdatacheck.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <ctype.h>
#include <errno.h>
#include <ftw.h>
#include <libgen.h>
@@ -29,47 +30,105 @@
#include "android-base/logging.h"
+static const char* BUNDLE_VERSION_FILENAME = "/bundle_version";
+// bundle_version is an ASCII file consisting of 17 bytes in the form: AAA.BBB|CCCCC|DDD
+// AAA.BBB is the major/minor version of the bundle format (e.g. 001.001),
+// CCCCC is the rules version (e.g. 2016g)
+// DDD is the android revision for this rules version to allow for bundle corrections (e.g. 001)
+// We only need the first 13 to determine if it is suitable for the device.
+static const int BUNDLE_VERSION_LENGTH = 13;
+// The major version of the bundle format supported by this code as a null-terminated char[].
+static const char REQUIRED_BUNDLE_VERSION[] = "001";
+static const size_t REQUIRED_BUNDLE_VERSION_LEN = sizeof(REQUIRED_BUNDLE_VERSION) - 1; // exclude \0
+// The length of the IANA rules version bytes. e.g. 2016a
+static const size_t RULES_VERSION_LEN = 5;
+// Bundle version bytes are: AAA.BBB|CCCCC - the rules version is CCCCC
+static const size_t BUNDLE_VERSION_RULES_IDX = 8;
+
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;
+// The major version of the bundle format supported by this code as a null-terminated char[].
+static const char TZ_DATA_HEADER_PREFIX[] = "tzdata";
+static const size_t TZ_DATA_HEADER_PREFIX_LEN = sizeof(TZ_DATA_HEADER_PREFIX) - 1; // exclude \0
+
static void usage() {
std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n"
"\n"
- "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";
+ "Checks whether any timezone update bundle in DATA_TZ_DIR is compatible with the\n"
+ "current Android release and better than or the same as base system timezone rules in\n"
+ "SYSTEM_TZ_DIR. If the timezone rules in SYSTEM_TZ_DIR are a higher version than the\n"
+ "one in DATA_TZ_DIR the DATA_TZ_DIR is renamed 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.
+ * Opens a file and fills buffer with the first byteCount bytes from the file.
+ * If the file does not exist or cannot be opened or is too short then 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;
+static bool readBytes(const std::string& fileName, char* buffer, size_t byteCount) {
+ FILE* file = fopen(fileName.c_str(), "r");
+ if (file == nullptr) {
+ if (errno != ENOENT) {
+ PLOG(WARNING) << "Error opening file " << fileName;
}
+ return false;
}
- size_t bytesRead = fread(headerBytes, 1, byteCount, tzDataFile);
+ size_t bytesRead = fread(buffer, 1, byteCount, file);
+ fclose(file);
if (bytesRead != byteCount) {
- LOG(FATAL) << tzDataFileName << " is too small. " << byteCount << " bytes required";
+ LOG(WARNING) << fileName << " is too small. " << byteCount << " bytes required";
+ return false;
}
- 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) {
+/*
+ * Checks the contents of headerBytes. Returns true if it is valid (starts with "tzdata"), false
+ * otherwise.
+ */
+static bool checkValidTzDataHeader(const std::string& fileName, const char* headerBytes) {
if (strncmp("tzdata", headerBytes, 6) != 0) {
- LOG(FATAL) << fileName << " does not start with the expected bytes (tzdata)";
+ LOG(WARNING) << fileName << " does not start with the expected bytes (tzdata)";
+ return false;
}
+ return true;
+}
+
+static bool checkDigits(const char* buffer, const size_t count, size_t* i) {
+ for (size_t j = 0; j < count; j++) {
+ char toCheck = buffer[(*i)++];
+ if (!isdigit(toCheck)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool checkValidBundleVersion(const char* buffer) {
+ // See BUNDLE_VERSION_LENGTH comments above for a description of the format.
+ size_t i = 0;
+ if (!checkDigits(buffer, 3, &i)) {
+ return false;
+ }
+ if (buffer[i++] != '.') {
+ return false;
+ }
+ if (!checkDigits(buffer, 3, &i)) {
+ return false;
+ }
+ if (buffer[i++] != '|') {
+ return false;
+ }
+ if (!checkDigits(buffer, 4, &i)) {
+ return false;
+ }
+ // Ignore the last character. It is assumed to be a letter but we don't check because it's not
+ // obvious what would happen at 'z'.
+ return true;
}
/* Return the parent directory of dirName. */
@@ -103,9 +162,24 @@
return 0;
}
+enum PathStatus { ERR, NONE, IS_DIR, NOT_DIR };
+
+static PathStatus checkPath(const std::string& path) {
+ struct stat buf;
+ if (stat(path.c_str(), &buf) != 0) {
+ if (errno != ENOENT) {
+ PLOG(WARNING) << "Unable to stat " << path;
+ return ERR;
+ }
+ return NONE;
+ }
+ return S_ISDIR(buf.st_mode) ? IS_DIR : NOT_DIR;
+}
+
/*
* 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.
+ * of the way. If dirToDelete does not exist this function does nothing and returns true. If
+ * dirToDelete is not a directory or cannot be accessed this method returns false.
*
* 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
@@ -114,23 +188,18 @@
*/
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";
+ int pathStatus = checkPath(dirToDelete);
+ if (pathStatus == NONE) {
+ LOG(INFO) << "Path " << dirToDelete << " does not exist";
+ return true;
+ }
+ if (pathStatus != IS_DIR) {
+ LOG(WARNING) << "Path " << dirToDelete << " failed to stat() or 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";
@@ -142,7 +211,7 @@
return false;
}
- // Rename dirToDelete to tempDirName.
+ // Rename dirToDelete to tempDirName (replacing the empty tempDirName directory created above).
int rc = rename(dirToDelete.c_str(), &tempDirName[0]);
if (rc == -1) {
PLOG(WARNING) << "Unable to rename directory from " << dirToDelete << " to "
@@ -151,6 +220,7 @@
}
// Recursively delete contents of tempDirName.
+
rc = nftw(&tempDirName[0], deleteFn, 10 /* openFiles */,
FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
if (rc == -1) {
@@ -160,9 +230,36 @@
}
/*
+ * Deletes the ConfigInstaller metadata directory.
+ * TODO(nfuller). http://b/31008728 Remove this when ConfigInstaller is no longer used.
+ */
+static void deleteConfigUpdaterMetadataDir(const char* dataZoneInfoDir) {
+ // 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";
+ }
+}
+
+/*
+ * Deletes the timezone update bundle directory.
+ */
+static void deleteUpdateBundleDir(std::string& bundleDirName) {
+ LOG(INFO) << "Removing: " << bundleDirName;
+ bool deleted = deleteDir(bundleDirName);
+ if (!deleted) {
+ LOG(WARNING) << "Deletion of bundle dir " << bundleDirName << " was not successful";
+ }
+}
+
+/*
* 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.
+ * version in /data.
*
* Note: This code is related to code in com.android.server.updates.TzDataInstallReceiver. The
* paths for the metadata and current timezone data must match.
@@ -175,62 +272,103 @@
int main(int argc, char* argv[]) {
if (argc != 3) {
usage();
+ return 1;
}
const char* systemZoneInfoDir = argv[1];
const char* dataZoneInfoDir = argv[2];
+ // Check the bundle directory exists. If it does not, exit quickly: nothing to do.
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.";
+ int dataCurrentDirStatus = checkPath(dataCurrentDirName);
+ if (dataCurrentDirStatus == NONE) {
+ LOG(INFO) << "timezone bundle dir " << dataCurrentDirName
+ << " does not exist. No action required.";
return 0;
}
- checkValidHeader(dataTzDataFileName, dataTzDataHeader.data());
+ // If the bundle directory path is not a directory or we can't stat() the path, exit with a
+ // warning: either there's a problem accessing storage or the world is not as it should be;
+ // nothing to do.
+ if (dataCurrentDirStatus != IS_DIR) {
+ LOG(WARNING) << "Current bundle dir " << dataCurrentDirName
+ << " could not be accessed or is not a directory. result=" << dataCurrentDirStatus;
+ return 2;
+ }
+ // Check the installed bundle version.
+ std::string bundleVersionFileName(dataCurrentDirName);
+ bundleVersionFileName += BUNDLE_VERSION_FILENAME;
+ std::vector<char> bundleVersion;
+ bundleVersion.reserve(BUNDLE_VERSION_LENGTH);
+ bool bundleVersionReadOk =
+ readBytes(bundleVersionFileName, bundleVersion.data(), BUNDLE_VERSION_LENGTH);
+ if (!bundleVersionReadOk) {
+ LOG(WARNING) << "bundle version file " << bundleVersionFileName
+ << " does not exist or is too short. Deleting bundle dir.";
+ // Implies the contents of the data partition is corrupt in some way. Try to clean up.
+ deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
+ deleteUpdateBundleDir(dataCurrentDirName);
+ return 3;
+ }
+
+ if (!checkValidBundleVersion(bundleVersion.data())) {
+ LOG(WARNING) << "bundle version file " << bundleVersionFileName
+ << " is not valid. Deleting bundle dir.";
+ // Implies the contents of the data partition is corrupt in some way. Try to clean up.
+ deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
+ deleteUpdateBundleDir(dataCurrentDirName);
+ return 4;
+ }
+
+ // Check the first 3 bytes of the bundleVersionHeader: these are the major version (e.g. 001).
+ // It must match exactly to be ok. The minor version is currently ignored.
+ if (strncmp(&bundleVersion[0], REQUIRED_BUNDLE_VERSION, REQUIRED_BUNDLE_VERSION_LEN) != 0) {
+ LOG(INFO) << "bundle version file " << bundleVersionFileName
+ << " is not the required version " << REQUIRED_BUNDLE_VERSION
+ << ". Deleting bundle dir.";
+ // This shouldn't happen with 001, but it in future, this will imply there has been an OTA
+ // and the installed bundle is not compatible with the new version of Android. Remove the
+ // installed bundle.
+ deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
+ deleteUpdateBundleDir(dataCurrentDirName);
+ return 5;
+ }
+
+ // Read the system rules version out of the /system tzdata file.
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);
+ readBytes(systemTzDataFileName, systemTzDataHeader.data(), TZ_HEADER_LENGTH);
if (!systemFileExists) {
- LOG(FATAL) << systemTzDataFileName << " does not exist or could not be opened";
+ // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
+ LOG(WARNING) << systemTzDataFileName << " does not exist or could not be opened";
+ return 6;
}
- 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";
- }
+ if (!checkValidTzDataHeader(systemTzDataFileName, systemTzDataHeader.data())) {
+ // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
+ LOG(WARNING) << systemTzDataFileName << " does not have a valid header.";
+ return 7;
}
+ // Compare the bundle rules version against the system rules version.
+ if (strncmp(
+ &systemTzDataHeader[TZ_DATA_HEADER_PREFIX_LEN],
+ &bundleVersion[BUNDLE_VERSION_RULES_IDX],
+ RULES_VERSION_LEN) <= 0) {
+ LOG(INFO) << "Found an installed bundle but it is valid. No action taken.";
+ // Implies there is an installed update, but it is good.
+ return 0;
+ }
+
+ // Implies there has been an OTA and the system version of the timezone rules is now newer
+ // than the version installed in /data. Remove the installed bundle.
+ LOG(INFO) << "timezone bundle in " << dataCurrentDirName << " is older than data in "
+ << systemTzDataFileName << "; fixing...";
+
+ deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
+ deleteUpdateBundleDir(dataCurrentDirName);
return 0;
}