Merge changes from topic 'early-mount-support'
* changes:
fs_mgr: add a generic fs_mgr_get_boot_config internal API
init: early_mount: create device node for verity metadata partition
init: early_mount: disallow partitions to be verified at boot
init: early_mount: add support to mount verity enabled partitions early
fs_mgr: make fs_mgr_setup_verity public API
fs_mgr: fix the fs_mgr_setup_verity param name
init: refactor to allow successive device_init calls
fs_mgr: add fs_mgr_do_mount_one() API
diff --git a/adb/Android.mk b/adb/Android.mk
index b444afa..c51e0b8 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -216,9 +216,9 @@
LOCAL_SRC_FILES_darwin := $(LIBADB_TEST_darwin_SRCS)
LOCAL_SRC_FILES_windows := $(LIBADB_TEST_windows_SRCS)
LOCAL_SANITIZE := $(adb_host_sanitize)
-LOCAL_SHARED_LIBRARIES := libbase
LOCAL_STATIC_LIBRARIES := \
libadb \
+ libbase \
libcrypto_utils \
libcrypto \
libcutils \
@@ -232,7 +232,7 @@
LOCAL_LDFLAGS_windows := -municode
LOCAL_LDLIBS_linux := -lrt -ldl -lpthread
LOCAL_LDLIBS_darwin := -framework CoreFoundation -framework IOKit -lobjc
-LOCAL_LDLIBS_windows := -lws2_32 -luserenv
+LOCAL_LDLIBS_windows := -lws2_32 -luserenv -static -lwinpthread
LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
LOCAL_MULTILIB := first
@@ -249,7 +249,7 @@
# Use wmain instead of main
LOCAL_LDFLAGS_windows := -municode
-LOCAL_LDLIBS_windows := -lws2_32 -lgdi32
+LOCAL_LDLIBS_windows := -lws2_32 -lgdi32 -static -lwinpthread
LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
LOCAL_REQUIRED_MODULES_windows := AdbWinApi AdbWinUsbApi
diff --git a/adb/adb.cpp b/adb/adb.cpp
index ece143c..577e9b9 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -1129,6 +1129,18 @@
return 0;
}
+#if ADB_HOST
+ if (!strcmp(service, "host-features")) {
+ FeatureSet features = supported_features();
+ // Abuse features to report libusb status.
+ if (should_use_libusb()) {
+ features.insert(kFeatureLibusb);
+ }
+ SendOkay(reply_fd, FeatureSetToString(features));
+ return 0;
+ }
+#endif
+
// remove TCP transport
if (!strncmp(service, "disconnect:", 11)) {
const std::string address(service + 11);
diff --git a/adb/adb.h b/adb/adb.h
index a30e297..aea5fb8 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -54,7 +54,7 @@
std::string adb_version();
// Increment this when we want to force users to start a new adb server.
-#define ADB_SERVER_VERSION 38
+#define ADB_SERVER_VERSION 39
class atransport;
diff --git a/adb/client/usb_dispatch.cpp b/adb/client/usb_dispatch.cpp
index f02dccf..bfc8e16 100644
--- a/adb/client/usb_dispatch.cpp
+++ b/adb/client/usb_dispatch.cpp
@@ -17,11 +17,6 @@
#include <android-base/logging.h>
#include "usb.h"
-static bool should_use_libusb() {
- static bool enable = getenv("ADB_LIBUSB") && strcmp(getenv("ADB_LIBUSB"), "1") == 0;
- return enable;
-}
-
void usb_init() {
if (should_use_libusb()) {
LOG(DEBUG) << "using libusb backend";
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 3b2df2e..5a2206f 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -1929,8 +1929,7 @@
else if (!strcmp(argv[0], "--version") || !strcmp(argv[0], "version")) {
fprintf(stdout, "%s", adb_version().c_str());
return 0;
- }
- else if (!strcmp(argv[0], "features")) {
+ } else if (!strcmp(argv[0], "features")) {
// Only list the features common to both the adb client and the device.
FeatureSet features;
std::string error;
@@ -1945,6 +1944,8 @@
}
}
return 0;
+ } else if (!strcmp(argv[0], "host-features")) {
+ return adb_query_command("host:host-features");
} else if (!strcmp(argv[0], "reconnect")) {
if (argc == 1) {
return adb_query_command("host:reconnect");
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index ac05d4c..c08b922 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -319,7 +319,7 @@
D("[ adb: cannot call endpoint alloc: errno=%d ]", errno);
// Kernel pre-allocation could have failed for recoverable reasons.
// Continue running with a safe max rw size.
- h->max_rw *= 2;
+ h->max_rw = USB_FFS_BULK_SIZE;
return true;
err:
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 60f3b5c..c951f5b 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -51,6 +51,7 @@
const char* const kFeatureShell2 = "shell_v2";
const char* const kFeatureCmd = "cmd";
const char* const kFeatureStat2 = "stat_v2";
+const char* const kFeatureLibusb = "libusb";
static std::string dump_packet(const char* name, const char* func, apacket* p) {
unsigned command = p->msg.command;
diff --git a/adb/transport.h b/adb/transport.h
index 3306388..490e513 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -47,6 +47,8 @@
// The 'cmd' command is available
extern const char* const kFeatureCmd;
extern const char* const kFeatureStat2;
+// The server is running with libusb enabled.
+extern const char* const kFeatureLibusb;
class atransport {
public:
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 3d6cc99..e16cf12 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -97,3 +97,12 @@
{
return (usb_class == ADB_CLASS && usb_subclass == ADB_SUBCLASS && usb_protocol == ADB_PROTOCOL);
}
+
+bool should_use_libusb() {
+#if defined(_WIN32) || !ADB_HOST
+ return false;
+#else
+ static bool enable = getenv("ADB_LIBUSB") && strcmp(getenv("ADB_LIBUSB"), "1") == 0;
+ return enable;
+#endif
+}
diff --git a/adb/usb.h b/adb/usb.h
index 879bacc..ba70de4 100644
--- a/adb/usb.h
+++ b/adb/usb.h
@@ -55,3 +55,5 @@
// USB device detection.
int is_adb_interface(int usb_class, int usb_subclass, int usb_protocol);
+
+bool should_use_libusb();
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index e275fa2..e2324b7 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -23,6 +23,7 @@
#error Only bionic supports system properties.
#endif
+#include <chrono>
#include <limits>
#include <string>
@@ -58,8 +59,12 @@
// tell you whether or not your call succeeded. A `false` return value definitely means failure.
bool SetProperty(const std::string& key, const std::string& value);
-// Waits for the system property `key` to have the value `expected_value`, .
-void WaitForProperty(const std::string& key, const std::string& expected_value);
+// Waits for the system property `key` to have the value `expected_value`.
+// Times out after `relative_timeout`.
+// Returns true on success, false on timeout.
+bool WaitForProperty(const std::string& key,
+ const std::string& expected_value,
+ std::chrono::milliseconds relative_timeout);
} // namespace base
} // namespace android
diff --git a/base/properties.cpp b/base/properties.cpp
index 0f0f638..acd6c0f 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -21,10 +21,14 @@
#include <sys/system_properties.h>
#include <sys/_system_properties.h>
+#include <algorithm>
+#include <chrono>
#include <string>
#include <android-base/parseint.h>
+using namespace std::chrono_literals;
+
namespace android {
namespace base {
@@ -96,14 +100,41 @@
}
}
-void WaitForProperty(const std::string& key, const std::string& expected_value) {
+// TODO: chrono_utils?
+static void DurationToTimeSpec(timespec& ts, std::chrono::nanoseconds d) {
+ auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
+ auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(d - s);
+ ts.tv_sec = s.count();
+ ts.tv_nsec = ns.count();
+}
+
+static void UpdateTimeSpec(timespec& ts,
+ const std::chrono::time_point<std::chrono::steady_clock>& timeout) {
+ auto now = std::chrono::steady_clock::now();
+ auto remaining_timeout = std::chrono::duration_cast<std::chrono::nanoseconds>(timeout - now);
+ if (remaining_timeout < 0ns) {
+ ts = { 0, 0 };
+ } else {
+ DurationToTimeSpec(ts, remaining_timeout);
+ }
+}
+
+bool WaitForProperty(const std::string& key,
+ const std::string& expected_value,
+ std::chrono::milliseconds relative_timeout) {
+ // TODO: boot_clock?
+ auto now = std::chrono::steady_clock::now();
+ std::chrono::time_point<std::chrono::steady_clock> absolute_timeout = now + relative_timeout;
+ timespec ts;
+
// Find the property's prop_info*.
const prop_info* pi;
unsigned global_serial = 0;
while ((pi = __system_property_find(key.c_str())) == nullptr) {
// The property doesn't even exist yet.
// Wait for a global change and then look again.
- global_serial = __system_property_wait_any(global_serial);
+ UpdateTimeSpec(ts, absolute_timeout);
+ if (!__system_property_wait(nullptr, global_serial, &global_serial, &ts)) return false;
}
WaitForPropertyData data;
@@ -112,10 +143,12 @@
while (true) {
// Check whether the property has the value we're looking for?
__system_property_read_callback(pi, WaitForPropertyCallback, &data);
- if (data.done) return;
+ if (data.done) return true;
- // It didn't so wait for it to change before checking again.
- __system_property_wait(pi, data.last_read_serial);
+ // It didn't, so wait for the property to change before checking again.
+ UpdateTimeSpec(ts, absolute_timeout);
+ uint32_t unused;
+ if (!__system_property_wait(pi, data.last_read_serial, &unused, &ts)) return false;
}
}
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
index d8186be..c68c2f8 100644
--- a/base/properties_test.cpp
+++ b/base/properties_test.cpp
@@ -134,8 +134,19 @@
android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
});
- android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a");
+ ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
flag = true;
- android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b");
+ ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", 1s));
thread.join();
}
+
+TEST(properties, WaitForProperty_timeout) {
+ auto t0 = std::chrono::steady_clock::now();
+ ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_timeout_test", "a",
+ 200ms));
+ auto t1 = std::chrono::steady_clock::now();
+
+ ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
+ // Upper bounds on timing are inherently flaky, but let's try...
+ ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
+}
diff --git a/init/Android.mk b/init/Android.mk
index a10a714..18cbedc 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -123,6 +123,7 @@
LOCAL_MODULE := init_tests
LOCAL_SRC_FILES := \
init_parser_test.cpp \
+ property_service_test.cpp \
util_test.cpp \
LOCAL_SHARED_LIBRARIES += \
diff --git a/init/README.md b/init/README.md
index c76a33b..d3dd73a 100644
--- a/init/README.md
+++ b/init/README.md
@@ -26,7 +26,7 @@
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
+extension. There 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
diff --git a/init/property_service.cpp b/init/property_service.cpp
index ce197ee..04bcb18 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -245,6 +245,13 @@
return true;
}
+ // http://b/35166374: don't allow init to make arbitrarily large allocations.
+ if (len > 0xffff) {
+ LOG(ERROR) << "sys_prop: RecvString asked to read huge string: " << len;
+ errno = ENOMEM;
+ return false;
+ }
+
std::vector<char> chars(len);
if (!RecvChars(&chars[0], len, timeout_ms)) {
return false;
@@ -386,12 +393,11 @@
return;
}
- /* Check socket options here */
struct ucred cr;
socklen_t cr_size = sizeof(cr);
if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
close(s);
- PLOG(ERROR) << "Unable to receive socket options";
+ PLOG(ERROR) << "sys_prop: unable to get SO_PEERCRED";
return;
}
@@ -399,14 +405,13 @@
uint32_t timeout_ms = kDefaultSocketTimeout;
uint32_t cmd = 0;
-
if (!socket.RecvUint32(&cmd, &timeout_ms)) {
PLOG(ERROR) << "sys_prop: error while reading command from the socket";
socket.SendUint32(PROP_ERROR_READ_CMD);
return;
}
- switch(cmd) {
+ switch (cmd) {
case PROP_MSG_SETPROP: {
char prop_name[PROP_NAME_MAX];
char prop_value[PROP_VALUE_MAX];
@@ -437,7 +442,9 @@
handle_property_set(socket, name, value, false);
break;
}
+
default:
+ LOG(ERROR) << "sys_prop: invalid command " << cmd;
socket.SendUint32(PROP_ERROR_INVALID_CMD);
break;
}
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
new file mode 100644
index 0000000..4d784aa
--- /dev/null
+++ b/init/property_service_test.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+#include <gtest/gtest.h>
+
+TEST(property_service, very_long_name_35166374) {
+ // Connect to the property service directly...
+ int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ ASSERT_NE(fd, -1);
+
+ static const char* property_service_socket = "/dev/socket/" PROP_SERVICE_NAME;
+ sockaddr_un addr = {};
+ addr.sun_family = AF_LOCAL;
+ strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path));
+
+ socklen_t addr_len = strlen(property_service_socket) + offsetof(sockaddr_un, sun_path) + 1;
+ ASSERT_NE(connect(fd, reinterpret_cast<sockaddr*>(&addr), addr_len), -1);
+
+ // ...so we can send it a malformed request.
+ uint32_t msg = PROP_MSG_SETPROP2;
+ uint32_t size = 0xffffffff;
+ uint32_t data = 0xdeadbeef;
+
+ ASSERT_EQ(static_cast<ssize_t>(sizeof(msg)), send(fd, &msg, sizeof(msg), 0));
+ ASSERT_EQ(static_cast<ssize_t>(sizeof(size)), send(fd, &size, sizeof(size), 0));
+ ASSERT_EQ(static_cast<ssize_t>(sizeof(data)), send(fd, &data, sizeof(data), 0));
+ ASSERT_EQ(0, close(fd));
+}
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 8ba7452..cf31195 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -60,7 +60,7 @@
"native_handle.c",
"open_memstream.c",
"record_stream.c",
- "sched_policy.c",
+ "sched_policy.cpp",
"sockets.cpp",
"strdup16to8.c",
"strdup8to16.c",
@@ -141,14 +141,6 @@
header_libs: ["libcutils_headers"],
export_header_lib_headers: ["libcutils_headers"],
- product_variables: {
- cpusets: {
- cflags: ["-DUSE_CPUSETS"],
- },
- schedboost: {
- cflags: ["-DUSE_SCHEDBOOST"],
- },
- },
cflags: [
"-Werror",
"-Wall",
diff --git a/libcutils/include/cutils/sched_policy.h b/libcutils/include/cutils/sched_policy.h
index 591bd44..15391d9 100644
--- a/libcutils/include/cutils/sched_policy.h
+++ b/libcutils/include/cutils/sched_policy.h
@@ -17,10 +17,27 @@
#ifndef __CUTILS_SCHED_POLICY_H
#define __CUTILS_SCHED_POLICY_H
+#include <stdbool.h>
+
#ifdef __cplusplus
extern "C" {
#endif
+/*
+ * Check if Linux kernel enables CPUSETS feature.
+ *
+ * Return value: 1 if Linux kernel CONFIG_CPUSETS=y; 0 otherwise.
+ */
+extern bool cpusets_enabled();
+
+/*
+ * Check if Linux kernel enables SCHEDTUNE feature (only available in Android
+ * common kernel or Linaro LSK, not in mainline Linux as of v4.9)
+ *
+ * Return value: 1 if Linux kernel CONFIG_SCHEDTUNE=y; 0 otherwise.
+ */
+extern bool schedboost_enabled();
+
/* Keep in sync with THREAD_GROUP_* in frameworks/base/core/java/android/os/Process.java */
typedef enum {
SP_DEFAULT = -1,
diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.cpp
similarity index 73%
rename from libcutils/sched_policy.c
rename to libcutils/sched_policy.cpp
index 5c5f3a5..7e3ad59 100644
--- a/libcutils/sched_policy.c
+++ b/libcutils/sched_policy.cpp
@@ -58,13 +58,11 @@
static int bg_cgroup_fd = -1;
static int fg_cgroup_fd = -1;
-#ifdef USE_CPUSETS
// File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error
static int system_bg_cpuset_fd = -1;
static int bg_cpuset_fd = -1;
static int fg_cpuset_fd = -1;
static int ta_cpuset_fd = -1; // special cpuset for top app
-#endif
// File descriptors open to /dev/stune/../tasks, setup by initialize, or -1 on error
static int bg_schedboost_fd = -1;
@@ -106,8 +104,53 @@
return 0;
}
-static void __initialize(void) {
- char* filename;
+/*
+ If CONFIG_CPUSETS for Linux kernel is set, "tasks" can be found under
+ /dev/cpuset mounted in init.rc; otherwise, that file does not exist
+ even though the directory, /dev/cpuset, is still created (by init.rc).
+
+ A couple of other candidates (under cpuset mount directory):
+ notify_on_release
+ release_agent
+
+ Yet another way to decide if cpuset is enabled is to parse
+ /proc/self/status and search for lines begin with "Mems_allowed".
+
+ If CONFIG_PROC_PID_CPUSET is set, the existence "/proc/self/cpuset" can
+ be used to decide if CONFIG_CPUSETS is set, so we don't have a dependency
+ on where init.rc mounts cpuset. That's why we'd better require this
+ configuration be set if CONFIG_CPUSETS is set.
+
+ With runtime check using the following function, build time
+ variables like ENABLE_CPUSETS (used in Android.mk) or cpusets (used
+ in Android.bp) are not needed.
+ */
+
+bool cpusets_enabled() {
+ static bool enabled = (access("/dev/cpuset/tasks", F_OK) == 0);
+
+ return enabled;
+}
+
+/*
+ Similar to CONFIG_CPUSETS above, but with a different configuration
+ CONFIG_SCHEDTUNE that's in Android common Linux kernel and Linaro
+ Stable Kernel (LSK), but not in mainline Linux as of v4.9.
+
+ With runtime check using the following function, build time
+ variables like ENABLE_SCHEDBOOST (used in Android.mk) or schedboost
+ (used in Android.bp) are not needed.
+
+ */
+
+bool schedboost_enabled() {
+ static bool enabled = (access("/dev/stune/tasks", F_OK) == 0);
+
+ return enabled;
+}
+
+static void __initialize() {
+ const char* filename;
if (!access("/dev/cpuctl/tasks", W_OK)) {
__sys_supports_schedgroups = 1;
@@ -126,28 +169,28 @@
__sys_supports_schedgroups = 0;
}
-#ifdef USE_CPUSETS
- if (!access("/dev/cpuset/tasks", W_OK)) {
+ if (cpusets_enabled()) {
+ if (!access("/dev/cpuset/tasks", W_OK)) {
- filename = "/dev/cpuset/foreground/tasks";
- fg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
- filename = "/dev/cpuset/background/tasks";
- bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
- filename = "/dev/cpuset/system-background/tasks";
- system_bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
- filename = "/dev/cpuset/top-app/tasks";
- ta_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ filename = "/dev/cpuset/foreground/tasks";
+ fg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ filename = "/dev/cpuset/background/tasks";
+ bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ filename = "/dev/cpuset/system-background/tasks";
+ system_bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ filename = "/dev/cpuset/top-app/tasks";
+ ta_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
-#ifdef USE_SCHEDBOOST
- filename = "/dev/stune/top-app/tasks";
- ta_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
- filename = "/dev/stune/foreground/tasks";
- fg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
- filename = "/dev/stune/background/tasks";
- bg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
-#endif
+ if (schedboost_enabled()) {
+ filename = "/dev/stune/top-app/tasks";
+ ta_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ filename = "/dev/stune/foreground/tasks";
+ fg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ filename = "/dev/stune/background/tasks";
+ bg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ }
+ }
}
-#endif
char buf[64];
snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", getpid());
@@ -235,33 +278,34 @@
if (__sys_supports_schedgroups) {
char grpBuf[32];
-#ifdef USE_CPUSETS
- if (getCGroupSubsys(tid, "cpuset", grpBuf, sizeof(grpBuf)) < 0)
- return -1;
- if (grpBuf[0] == '\0') {
- *policy = SP_FOREGROUND;
- } else if (!strcmp(grpBuf, "foreground")) {
- *policy = SP_FOREGROUND;
- } else if (!strcmp(grpBuf, "background")) {
- *policy = SP_BACKGROUND;
- } else if (!strcmp(grpBuf, "top-app")) {
- *policy = SP_TOP_APP;
+
+ if (cpusets_enabled()) {
+ if (getCGroupSubsys(tid, "cpuset", grpBuf, sizeof(grpBuf)) < 0)
+ return -1;
+ if (grpBuf[0] == '\0') {
+ *policy = SP_FOREGROUND;
+ } else if (!strcmp(grpBuf, "foreground")) {
+ *policy = SP_FOREGROUND;
+ } else if (!strcmp(grpBuf, "background")) {
+ *policy = SP_BACKGROUND;
+ } else if (!strcmp(grpBuf, "top-app")) {
+ *policy = SP_TOP_APP;
+ } else {
+ errno = ERANGE;
+ return -1;
+ }
} else {
- errno = ERANGE;
- return -1;
+ if (getCGroupSubsys(tid, "cpu", grpBuf, sizeof(grpBuf)) < 0)
+ return -1;
+ if (grpBuf[0] == '\0') {
+ *policy = SP_FOREGROUND;
+ } else if (!strcmp(grpBuf, "bg_non_interactive")) {
+ *policy = SP_BACKGROUND;
+ } else {
+ errno = ERANGE;
+ return -1;
+ }
}
-#else
- if (getCGroupSubsys(tid, "cpu", grpBuf, sizeof(grpBuf)) < 0)
- return -1;
- if (grpBuf[0] == '\0') {
- *policy = SP_FOREGROUND;
- } else if (!strcmp(grpBuf, "bg_non_interactive")) {
- *policy = SP_BACKGROUND;
- } else {
- errno = ERANGE;
- return -1;
- }
-#endif
} else {
int rc = sched_getscheduler(tid);
if (rc < 0)
@@ -281,9 +325,10 @@
int set_cpuset_policy(int tid, SchedPolicy policy)
{
// in the absence of cpusets, use the old sched policy
-#ifndef USE_CPUSETS
- return set_sched_policy(tid, policy);
-#else
+ if (!cpusets_enabled()) {
+ return set_sched_policy(tid, policy);
+ }
+
if (tid == 0) {
tid = gettid();
}
@@ -320,15 +365,14 @@
return -errno;
}
-#ifdef USE_SCHEDBOOST
- if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
- if (errno != ESRCH && errno != ENOENT)
- return -errno;
+ if (schedboost_enabled()) {
+ if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
+ if (errno != ESRCH && errno != ENOENT)
+ return -errno;
+ }
}
-#endif
return 0;
-#endif
}
static void set_timerslack_ns(int tid, unsigned long long slack) {
@@ -420,18 +464,17 @@
break;
}
-
if (add_tid_to_cgroup(tid, fd) != 0) {
if (errno != ESRCH && errno != ENOENT)
return -errno;
}
-#ifdef USE_SCHEDBOOST
- if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
- if (errno != ESRCH && errno != ENOENT)
- return -errno;
+ if (schedboost_enabled()) {
+ if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
+ if (errno != ESRCH && errno != ENOENT)
+ return -errno;
+ }
}
-#endif
} else {
struct sched_param param;
diff --git a/liblog/logd_reader.c b/liblog/logd_reader.c
index ccc7da8..a6c3f7a 100644
--- a/liblog/logd_reader.c
+++ b/liblog/logd_reader.c
@@ -91,7 +91,7 @@
static int logdAvailable(log_id_t logId)
{
- if (logId > LOG_ID_KERNEL) {
+ if (logId >= LOG_ID_MAX) {
return -EINVAL;
}
if (logId == LOG_ID_SECURITY) {
diff --git a/liblog/logd_writer.c b/liblog/logd_writer.c
index 2bab92e..12b797d 100644
--- a/liblog/logd_writer.c
+++ b/liblog/logd_writer.c
@@ -117,7 +117,7 @@
static int logdAvailable(log_id_t logId)
{
- if (logId > LOG_ID_SECURITY) {
+ if (logId >= LOG_ID_MAX || logId == LOG_ID_KERNEL) {
return -EINVAL;
}
if (atomic_load(&logdLoggerWrite.context.sock) < 0) {
diff --git a/liblog/logprint.c b/liblog/logprint.c
index e61850d..af52528 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -625,14 +625,14 @@
}
static bool findChar(const char** cp, size_t* len, int c) {
- while (*len && isspace(**cp)) {
- ++*cp;
- --*len;
+ while ((*len) && isspace(*(*cp))) {
+ ++(*cp);
+ --(*len);
}
if (c == INT_MAX) return *len;
- if (*len && (**cp == c)) {
- ++*cp;
- --*len;
+ if ((*len) && (*(*cp) == c)) {
+ ++(*cp);
+ --(*len);
return true;
}
return false;
@@ -991,7 +991,7 @@
LIBLOG_ABI_PUBLIC int android_log_processBinaryLogBuffer(
struct logger_entry *buf,
AndroidLogEntry *entry,
- const EventTagMap *map __unused,
+ const EventTagMap *map __unused, // only on !__ANDROID__
char *messageBuf, int messageBufLen)
{
size_t inCount;
diff --git a/logcat/.clang-format b/logcat/.clang-format
new file mode 100644
index 0000000..393c309
--- /dev/null
+++ b/logcat/.clang-format
@@ -0,0 +1,11 @@
+BasedOnStyle: Google
+AllowShortFunctionsOnASingleLine: false
+
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+PenaltyExcessCharacter: 32
+
+Cpp11BracedListStyle: false
diff --git a/logcat/Android.mk b/logcat/Android.mk
index 1dacbe1..723c35e 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -1,20 +1,31 @@
# Copyright 2006-2014 The Android Open Source Project
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
+
+logcatLibs := liblog libbase libcutils libpcrecpp
+
include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= logcat.cpp event.logtags
-
-LOCAL_SHARED_LIBRARIES := liblog libbase libcutils libpcrecpp
-
LOCAL_MODULE := logcat
-
+LOCAL_SRC_FILES := logcat_main.cpp event.logtags
+LOCAL_SHARED_LIBRARIES := liblogcat $(logcatLibs)
LOCAL_CFLAGS := -Werror
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
+LOCAL_MODULE := liblogcat
+LOCAL_SRC_FILES := logcat.cpp logcat_system.cpp
+LOCAL_SHARED_LIBRARIES := $(logcatLibs)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Werror
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
LOCAL_MODULE := logpersist.start
LOCAL_MODULE_TAGS := debug
LOCAL_MODULE_CLASS := EXECUTABLES
diff --git a/logcat/include/log/logcat.h b/logcat/include/log/logcat.h
new file mode 100644
index 0000000..009672c
--- /dev/null
+++ b/logcat/include/log/logcat.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2005-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_LOGCAT_H /* header boilerplate */
+#define _LIBS_LOGCAT_H
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
+#elif __ANDROID_API__ > 24 /* > Nougat */
+#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
+
+/* For managing an in-process logcat function, rather than forking/execing
+ *
+ * It also serves as the basis for the logcat command.
+ *
+ * The following C API allows a logcat instance to be created, run
+ * to completion, and then release all the associated resources.
+ */
+
+/*
+ * The opaque context
+ */
+#ifndef __android_logcat_context_defined /* typedef boilerplate */
+#define __android_logcat_context_defined
+typedef struct android_logcat_context_internal* android_logcat_context;
+#endif
+
+/* Creates a context associated with this logcat instance
+ *
+ * Returns a pointer to the context, or a NULL on error.
+ */
+android_logcat_context create_android_logcat();
+
+/* Collects and outputs the logcat data to output and error file descriptors
+ *
+ * Will block, performed in-thread and in-process
+ *
+ * The output file descriptor variable, if greater than or equal to 0, is
+ * where the output (ie: stdout) will be sent. The file descriptor is closed
+ * on android_logcat_destroy which terminates the instance, or when an -f flag
+ * (output redirect to a file) is present in the command. The error file
+ * descriptor variable, if greater than or equal to 0, is where the error
+ * stream (ie: stderr) will be sent, also closed on android_logcat_destroy.
+ * The error file descriptor can be set to equal to the output file descriptor,
+ * which will mix output and error stream content, and will defer closure of
+ * the file descriptor on -f flag redirection. Negative values for the file
+ * descriptors will use stdout and stderr FILE references respectively
+ * internally, and will not close the references as noted above.
+ *
+ * Return value is 0 for success, non-zero for errors.
+ */
+int android_logcat_run_command(android_logcat_context ctx, int output, int error,
+ int argc, char* const* argv, char* const* envp);
+
+/* Will not block, performed in-process
+ *
+ * Starts a thread, opens a pipe, returns reading end fd, saves off argv.
+ * The command supports 2>&1 (mix content) and 2>/dev/null (drop content) for
+ * scripted error (stderr) redirection.
+ */
+int android_logcat_run_command_thread(android_logcat_context ctx, int argc,
+ char* const* argv, char* const* envp);
+int android_logcat_run_command_thread_running(android_logcat_context ctx);
+
+/* Finished with context
+ *
+ * Kill the command thread ASAP (if any), and free up all associated resources.
+ *
+ * Return value is the result of the android_logcat_run_command, or
+ * non-zero for any errors.
+ */
+int android_logcat_destroy(android_logcat_context* ctx);
+
+/* derived helpers */
+
+/*
+ * In-process thread that acts like somewhat like libc-like system and popen
+ * respectively. Can not handle shell scripting, only pure calls to the
+ * logcat operations. The android_logcat_system is a wrapper for the
+ * create_android_logcat, android_logcat_run_command and android_logcat_destroy
+ * API above. The android_logcat_popen is a wrapper for the
+ * android_logcat_run_command_thread API above. The android_logcat_pclose is
+ * a wrapper for a reasonable wait until output has subsided for command
+ * completion, fclose on the FILE pointer and the android_logcat_destroy API.
+ */
+int android_logcat_system(const char* command);
+FILE* android_logcat_popen(android_logcat_context* ctx, const char* command);
+int android_logcat_pclose(android_logcat_context* ctx, FILE* output);
+
+#endif /* __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOGCAT_H */
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 4a171fd..7f852d4 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -1,4 +1,18 @@
-// Copyright 2006-2015 The Android Open Source Project
+/*
+ * Copyright (C) 2006-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
#include <arpa/inet.h>
#include <assert.h>
@@ -8,8 +22,8 @@
#include <fcntl.h>
#include <getopt.h>
#include <math.h>
+#include <pthread.h>
#include <sched.h>
-#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -22,8 +36,10 @@
#include <time.h>
#include <unistd.h>
+#include <atomic>
#include <memory>
#include <string>
+#include <vector>
#include <android-base/file.h>
#include <android-base/stringprintf.h>
@@ -31,6 +47,7 @@
#include <cutils/sched_policy.h>
#include <cutils/sockets.h>
#include <log/event_tag_map.h>
+#include <log/logcat.h>
#include <log/logprint.h>
#include <private/android_logger.h>
#include <system/thread_defs.h>
@@ -39,16 +56,82 @@
#define DEFAULT_MAX_ROTATED_LOGS 4
-static AndroidLogFormat * g_logformat;
+struct android_logcat_context_internal {
+ // status
+ volatile std::atomic_int retval; // valid if thread_stopped set
+ // Arguments passed in, or copies and storage thereof if a thread.
+ int argc;
+ char* const* argv;
+ char* const* envp;
+ std::vector<std::string> args;
+ std::vector<const char*> argv_hold;
+ std::vector<std::string> envs;
+ std::vector<const char*> envp_hold;
+ int output_fd;
+ int error_fd;
-/* logd prefixes records with a length field */
+ // library
+ int fds[2]; // From popen call
+ FILE* output; // everything writes to fileno(output), buffer unused
+ FILE* error; // unless error == output.
+ pthread_t thr;
+ volatile std::atomic_bool stop; // quick exit flag
+ volatile std::atomic_bool thread_stopped;
+ bool stderr_null; // shell "2>/dev/null"
+ bool stderr_stdout; // shell "2>&1"
+
+ // global variables
+ AndroidLogFormat* logformat;
+ const char* outputFileName;
+ // 0 means "no log rotation"
+ size_t logRotateSizeKBytes;
+ // 0 means "unbounded"
+ size_t maxRotatedLogs;
+ size_t outByteCount;
+ int printBinary;
+ int devCount; // >1 means multiple
+ pcrecpp::RE* regex;
+ // 0 means "infinite"
+ size_t maxCount;
+ size_t printCount;
+ bool printItAnyways;
+ bool debug;
+
+ // static variables
+ bool hasOpenedEventTagMap;
+ EventTagMap* eventTagMap;
+};
+
+// Creates a context associated with this logcat instance
+android_logcat_context create_android_logcat() {
+ android_logcat_context_internal* context;
+
+ context = (android_logcat_context_internal*)calloc(
+ 1, sizeof(android_logcat_context_internal));
+ if (!context) return NULL;
+
+ context->fds[0] = -1;
+ context->fds[1] = -1;
+ context->output_fd = -1;
+ context->error_fd = -1;
+ context->maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS;
+
+ context->argv_hold.clear();
+ context->args.clear();
+ context->envp_hold.clear();
+ context->envs.clear();
+
+ return (android_logcat_context)context;
+}
+
+// logd prefixes records with a length field
#define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t)
struct log_device_t {
const char* device;
bool binary;
- struct logger *logger;
- struct logger_list *logger_list;
+ struct logger* logger;
+ struct logger_list* logger_list;
bool printed;
log_device_t* next;
@@ -65,64 +148,121 @@
namespace android {
-/* Global Variables */
-
-static const char * g_outputFileName;
-// 0 means "no log rotation"
-static size_t g_logRotateSizeKBytes;
-// 0 means "unbounded"
-static size_t g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS;
-static int g_outFD = -1;
-static size_t g_outByteCount;
-static int g_printBinary;
-static int g_devCount; // >1 means multiple
-static pcrecpp::RE* g_regex;
-// 0 means "infinite"
-static size_t g_maxCount;
-static size_t g_printCount;
-static bool g_printItAnyways;
-static bool g_debug;
-
-enum helpType {
- HELP_FALSE,
- HELP_TRUE,
- HELP_FORMAT
-};
+enum helpType { HELP_FALSE, HELP_TRUE, HELP_FORMAT };
// if showHelp is set, newline required in fmt statement to transition to usage
-__noreturn static void logcat_panic(enum helpType showHelp, const char *fmt, ...) __printflike(2,3);
+static void logcat_panic(android_logcat_context_internal* context,
+ enum helpType showHelp, const char* fmt, ...)
+ __printflike(3, 4);
-static int openLogFile (const char *pathname)
-{
+static int openLogFile(const char* pathname) {
return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
}
-static void rotateLogs()
-{
+static void close_output(android_logcat_context_internal* context) {
+ // split output_from_error
+ if (context->error == context->output) {
+ context->output = NULL;
+ context->output_fd = -1;
+ }
+ if (context->error && (context->output_fd == fileno(context->error))) {
+ context->output_fd = -1;
+ }
+ if (context->output_fd == context->error_fd) {
+ context->output_fd = -1;
+ }
+ // close output channel
+ if (context->output) {
+ if (context->output != stdout) {
+ if (context->output_fd == fileno(context->output)) {
+ context->output_fd = -1;
+ }
+ if (context->fds[1] == fileno(context->output)) {
+ context->fds[1] = -1;
+ }
+ fclose(context->output);
+ }
+ context->output = NULL;
+ }
+ if (context->output_fd >= 0) {
+ if (context->output_fd != fileno(stdout)) {
+ if (context->fds[1] == context->output_fd) {
+ context->fds[1] = -1;
+ }
+ close(context->output_fd);
+ }
+ context->output_fd = -1;
+ }
+}
+
+static void close_error(android_logcat_context_internal* context) {
+ // split error_from_output
+ if (context->output == context->error) {
+ context->error = NULL;
+ context->error_fd = -1;
+ }
+ if (context->output && (context->error_fd == fileno(context->output))) {
+ context->error_fd = -1;
+ }
+ if (context->error_fd == context->output_fd) {
+ context->error_fd = -1;
+ }
+ // close error channel
+ if (context->error) {
+ if ((context->error != stderr) && (context->error != stdout)) {
+ if (context->error_fd == fileno(context->error)) {
+ context->error_fd = -1;
+ }
+ if (context->fds[1] == fileno(context->error)) {
+ context->fds[1] = -1;
+ }
+ fclose(context->error);
+ }
+ context->error = NULL;
+ }
+ if (context->error_fd >= 0) {
+ if ((context->error_fd != fileno(stdout)) &&
+ (context->error_fd != fileno(stderr))) {
+ if (context->fds[1] == context->error_fd) {
+ context->fds[1] = -1;
+ }
+ close(context->error_fd);
+ }
+ context->error_fd = -1;
+ }
+}
+
+static void rotateLogs(android_logcat_context_internal* context) {
int err;
// Can't rotate logs if we're not outputting to a file
- if (g_outputFileName == NULL) {
+ if (context->outputFileName == NULL) {
return;
}
- close(g_outFD);
+ close_output(context);
- // Compute the maximum number of digits needed to count up to g_maxRotatedLogs in decimal.
- // eg: g_maxRotatedLogs == 30 -> log10(30) == 1.477 -> maxRotationCountDigits == 2
+ // Compute the maximum number of digits needed to count up to
+ // maxRotatedLogs in decimal. eg:
+ // maxRotatedLogs == 30
+ // -> log10(30) == 1.477
+ // -> maxRotationCountDigits == 2
int maxRotationCountDigits =
- (g_maxRotatedLogs > 0) ? (int) (floor(log10(g_maxRotatedLogs) + 1)) : 0;
+ (context->maxRotatedLogs > 0)
+ ? (int)(floor(log10(context->maxRotatedLogs) + 1))
+ : 0;
- for (int i = g_maxRotatedLogs ; i > 0 ; i--) {
+ for (int i = context->maxRotatedLogs; i > 0; i--) {
std::string file1 = android::base::StringPrintf(
- "%s.%.*d", g_outputFileName, maxRotationCountDigits, i);
+ "%s.%.*d", context->outputFileName, maxRotationCountDigits, i);
std::string file0;
if (i - 1 == 0) {
- file0 = android::base::StringPrintf("%s", g_outputFileName);
+ file0 = android::base::StringPrintf("%s", context->outputFileName);
} else {
- file0 = android::base::StringPrintf(
- "%s.%.*d", g_outputFileName, maxRotationCountDigits, i - 1);
+ file0 =
+ android::base::StringPrintf("%s.%.*d", context->outputFileName,
+ maxRotationCountDigits, i - 1);
}
if ((file0.length() == 0) || (file1.length() == 0)) {
@@ -137,153 +277,160 @@
}
}
- g_outFD = openLogFile(g_outputFileName);
+ context->output_fd = openLogFile(context->outputFileName);
- if (g_outFD < 0) {
- logcat_panic(HELP_FALSE, "couldn't open output file");
+ if (context->output_fd < 0) {
+ logcat_panic(context, HELP_FALSE, "couldn't open output file");
+ return;
}
+ context->output = fdopen(context->output_fd, "web");
- g_outByteCount = 0;
-
+ context->outByteCount = 0;
}
-void printBinary(struct log_msg *buf)
-{
+void printBinary(android_logcat_context_internal* context, struct log_msg* buf) {
size_t size = buf->len();
- TEMP_FAILURE_RETRY(write(g_outFD, buf, size));
+ TEMP_FAILURE_RETRY(write(context->output_fd, buf, size));
}
-static bool regexOk(const AndroidLogEntry& entry)
-{
- if (!g_regex) {
+static bool regexOk(android_logcat_context_internal* context,
+ const AndroidLogEntry& entry) {
+ if (!context->regex) {
return true;
}
std::string messageString(entry.message, entry.messageLen);
- return g_regex->PartialMatch(messageString);
+ return context->regex->PartialMatch(messageString);
}
-static void processBuffer(log_device_t* dev, struct log_msg *buf)
-{
+static void processBuffer(android_logcat_context_internal* context,
+ log_device_t* dev, struct log_msg* buf) {
int bytesWritten = 0;
int err;
AndroidLogEntry entry;
char binaryMsgBuf[1024];
if (dev->binary) {
- static bool hasOpenedEventTagMap = false;
- static EventTagMap *eventTagMap = NULL;
-
- if (!eventTagMap && !hasOpenedEventTagMap) {
- eventTagMap = android_openEventTagMap(NULL);
- hasOpenedEventTagMap = true;
+ if (!context->eventTagMap && !context->hasOpenedEventTagMap) {
+ context->eventTagMap = android_openEventTagMap(NULL);
+ context->hasOpenedEventTagMap = true;
}
- err = android_log_processBinaryLogBuffer(&buf->entry_v1, &entry,
- eventTagMap,
- binaryMsgBuf,
- sizeof(binaryMsgBuf));
- //printf(">>> pri=%d len=%d msg='%s'\n",
+ err = android_log_processBinaryLogBuffer(
+ &buf->entry_v1, &entry, context->eventTagMap, binaryMsgBuf,
+ sizeof(binaryMsgBuf));
+ // printf(">>> pri=%d len=%d msg='%s'\n",
// entry.priority, entry.messageLen, entry.message);
} else {
err = android_log_processLogBuffer(&buf->entry_v1, &entry);
}
- if ((err < 0) && !g_debug) {
- goto error;
+ if ((err < 0) && !context->debug) {
+ return;
}
- if (android_log_shouldPrintLine(g_logformat,
- std::string(entry.tag, entry.tagLen).c_str(),
- entry.priority)) {
- bool match = regexOk(entry);
+ if (android_log_shouldPrintLine(
+ context->logformat, std::string(entry.tag, entry.tagLen).c_str(),
+ entry.priority)) {
+ bool match = regexOk(context, entry);
- g_printCount += match;
- if (match || g_printItAnyways) {
- bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry);
+ context->printCount += match;
+ if (match || context->printItAnyways) {
+ bytesWritten = android_log_printLogLine(context->logformat,
+ context->output_fd, &entry);
if (bytesWritten < 0) {
- logcat_panic(HELP_FALSE, "output error");
+ logcat_panic(context, HELP_FALSE, "output error");
+ return;
}
}
}
- g_outByteCount += bytesWritten;
+ context->outByteCount += bytesWritten;
- if (g_logRotateSizeKBytes > 0
- && (g_outByteCount / 1024) >= g_logRotateSizeKBytes
- ) {
- rotateLogs();
+ if (context->logRotateSizeKBytes > 0 &&
+ (context->outByteCount / 1024) >= context->logRotateSizeKBytes) {
+ rotateLogs(context);
}
-
-error:
- return;
}
-static void maybePrintStart(log_device_t* dev, bool printDividers) {
+static void maybePrintStart(android_logcat_context_internal* context,
+ log_device_t* dev, bool printDividers) {
if (!dev->printed || printDividers) {
- if (g_devCount > 1 && !g_printBinary) {
+ if (context->devCount > 1 && !context->printBinary) {
char buf[1024];
snprintf(buf, sizeof(buf), "--------- %s %s\n",
- dev->printed ? "switch to" : "beginning of",
- dev->device);
- if (write(g_outFD, buf, strlen(buf)) < 0) {
- logcat_panic(HELP_FALSE, "output error");
+ dev->printed ? "switch to" : "beginning of", dev->device);
+ if (write(context->output_fd, buf, strlen(buf)) < 0) {
+ logcat_panic(context, HELP_FALSE, "output error");
+ return;
}
}
dev->printed = true;
}
}
-static void setupOutputAndSchedulingPolicy(bool blocking) {
- if (g_outputFileName == NULL) {
- g_outFD = STDOUT_FILENO;
- return;
- }
+static void setupOutputAndSchedulingPolicy(
+ android_logcat_context_internal* context, bool blocking) {
+ if (context->outputFileName == NULL) return;
if (blocking) {
// Lower priority and set to batch scheduling if we are saving
// the logs into files and taking continuous content.
- if (set_sched_policy(0, SP_BACKGROUND) < 0) {
- fprintf(stderr, "failed to set background scheduling policy\n");
+ if ((set_sched_policy(0, SP_BACKGROUND) < 0) && context->error) {
+ fprintf(context->error,
+ "failed to set background scheduling policy\n");
}
struct sched_param param;
memset(¶m, 0, sizeof(param));
- if (sched_setscheduler((pid_t) 0, SCHED_BATCH, ¶m) < 0) {
+ if (sched_setscheduler((pid_t)0, SCHED_BATCH, ¶m) < 0) {
fprintf(stderr, "failed to set to batch scheduler\n");
}
- if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
- fprintf(stderr, "failed set to priority\n");
+ if ((setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) &&
+ context->error) {
+ fprintf(context->error, "failed set to priority\n");
}
}
- g_outFD = openLogFile (g_outputFileName);
+ close_output(context);
- if (g_outFD < 0) {
- logcat_panic(HELP_FALSE, "couldn't open output file");
+ context->output_fd = openLogFile(context->outputFileName);
+
+ if (context->output_fd < 0) {
+ logcat_panic(context, HELP_FALSE, "couldn't open output file");
+ return;
}
struct stat statbuf;
- if (fstat(g_outFD, &statbuf) == -1) {
- close(g_outFD);
- logcat_panic(HELP_FALSE, "couldn't get output file stat\n");
+ if (fstat(context->output_fd, &statbuf) == -1) {
+ close_output(context);
+ logcat_panic(context, HELP_FALSE, "couldn't get output file stat\n");
+ return;
}
- if ((size_t) statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
- close(g_outFD);
- logcat_panic(HELP_FALSE, "invalid output file stat\n");
+ if ((size_t)statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
+ close_output(context);
+ logcat_panic(context, HELP_FALSE, "invalid output file stat\n");
+ return;
}
- g_outByteCount = statbuf.st_size;
+ context->output = fdopen(context->output_fd, "web");
+
+ context->outByteCount = statbuf.st_size;
}
-static void show_help(const char *cmd)
-{
- fprintf(stderr,"Usage: %s [options] [filterspecs]\n", cmd);
+// clang-format off
+static void show_help(android_logcat_context_internal* context) {
+ if (!context->error) return;
- fprintf(stderr, "options include:\n"
+ const char* cmd = strrchr(context->argv[0], '/');
+ cmd = cmd ? cmd + 1 : context->argv[0];
+
+ fprintf(context->error, "Usage: %s [options] [filterspecs]\n", cmd);
+
+ fprintf(context->error, "options include:\n"
" -s Set default filter to silent. Equivalent to filterspec '*:S'\n"
" -f <file>, --file=<file> Log to file. Default is stdout\n"
" -r <kbytes>, --rotate-kbytes=<kbytes>\n"
@@ -347,7 +494,7 @@
" comes first. Improves efficiency of polling by providing\n"
" an about-to-wrap wakeup.\n");
- fprintf(stderr,"\nfilterspecs are a series of \n"
+ fprintf(context->error, "\nfilterspecs are a series of \n"
" <tag>[:priority]\n\n"
"where <tag> is a log component tag (or * for all) and priority is:\n"
" V Verbose (default for <tag>)\n"
@@ -365,9 +512,9 @@
"or defaults to \"threadtime\"\n\n");
}
-static void show_format_help()
-{
- fprintf(stderr,
+static void show_format_help(android_logcat_context_internal* context) {
+ if (!context->error) return;
+ fprintf(context->error,
"-v <format>, --format=<format> options:\n"
" Sets log print format verb and adverbs, where <format> is:\n"
" brief long process raw tag thread threadtime time\n"
@@ -398,10 +545,11 @@
" \"<zone>\" — Print using this public named timezone (experimental).\n\n"
);
}
+// clang-format on
-static int setLogFormat(const char * formatString)
-{
- static AndroidLogPrintFormat format;
+static int setLogFormat(android_logcat_context_internal* context,
+ const char* formatString) {
+ AndroidLogPrintFormat format;
format = android_log_formatFromString(formatString);
@@ -410,42 +558,36 @@
return -1;
}
- return android_log_setPrintFormat(g_logformat, format);
+ return android_log_setPrintFormat(context->logformat, format);
}
-static const char multipliers[][2] = {
- { "" },
- { "K" },
- { "M" },
- { "G" }
-};
+static const char multipliers[][2] = { { "" }, { "K" }, { "M" }, { "G" } };
-static unsigned long value_of_size(unsigned long value)
-{
+static unsigned long value_of_size(unsigned long value) {
for (unsigned i = 0;
- (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024);
- value /= 1024, ++i) ;
+ (i < sizeof(multipliers) / sizeof(multipliers[0])) && (value >= 1024);
+ value /= 1024, ++i)
+ ;
return value;
}
-static const char *multiplier_of_size(unsigned long value)
-{
+static const char* multiplier_of_size(unsigned long value) {
unsigned i;
for (i = 0;
- (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024);
- value /= 1024, ++i) ;
+ (i < sizeof(multipliers) / sizeof(multipliers[0])) && (value >= 1024);
+ value /= 1024, ++i)
+ ;
return multipliers[i];
}
-/*String to unsigned int, returns -1 if it fails*/
-static bool getSizeTArg(const char *ptr, size_t *val, size_t min = 0,
- size_t max = SIZE_MAX)
-{
+// String to unsigned int, returns -1 if it fails
+static bool getSizeTArg(const char* ptr, size_t* val, size_t min = 0,
+ size_t max = SIZE_MAX) {
if (!ptr) {
return false;
}
- char *endp;
+ char* endp;
errno = 0;
size_t ret = (size_t)strtoll(ptr, &endp, 0);
@@ -461,31 +603,36 @@
return true;
}
-static void logcat_panic(enum helpType showHelp, const char *fmt, ...)
-{
- va_list args;
+static void logcat_panic(android_logcat_context_internal* context,
+ enum helpType showHelp, const char* fmt, ...) {
+ context->retval = EXIT_FAILURE;
+ if (!context->error) {
+ context->stop = true;
+ return;
+ }
+
+ va_list args;
va_start(args, fmt);
- vfprintf(stderr, fmt, args);
+ vfprintf(context->error, fmt, args);
va_end(args);
switch (showHelp) {
- case HELP_TRUE:
- show_help(getprogname());
- break;
- case HELP_FORMAT:
- show_format_help();
- break;
- case HELP_FALSE:
- default:
- break;
+ case HELP_TRUE:
+ show_help(context);
+ break;
+ case HELP_FORMAT:
+ show_format_help(context);
+ break;
+ case HELP_FALSE:
+ default:
+ break;
}
- exit(EXIT_FAILURE);
+ context->stop = true;
}
-static char *parseTime(log_time &t, const char *cp) {
-
- char *ep = t.strptime(cp, "%m-%d %H:%M:%S.%q");
+static char* parseTime(log_time& t, const char* cp) {
+ char* ep = t.strptime(cp, "%m-%d %H:%M:%S.%q");
if (ep) {
return ep;
}
@@ -497,14 +644,14 @@
}
// Find last logged line in <outputFileName>, or <outputFileName>.1
-static log_time lastLogTime(char *outputFileName) {
+static log_time lastLogTime(char* outputFileName) {
log_time retval(log_time::EPOCH);
if (!outputFileName) {
return retval;
}
std::string directory;
- char *file = strrchr(outputFileName, '/');
+ char* file = strrchr(outputFileName, '/');
if (!file) {
directory = ".";
file = outputFileName;
@@ -515,8 +662,8 @@
++file;
}
- std::unique_ptr<DIR, int(*)(DIR*)>
- dir(opendir(directory.c_str()), closedir);
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(directory.c_str()),
+ closedir);
if (!dir.get()) {
return retval;
}
@@ -525,14 +672,12 @@
size_t len = strlen(file);
log_time modulo(0, NS_PER_SEC);
- struct dirent *dp;
+ struct dirent* dp;
while ((dp = readdir(dir.get())) != NULL) {
- if ((dp->d_type != DT_REG) ||
- (strncmp(dp->d_name, file, len) != 0) ||
- (dp->d_name[len] &&
- ((dp->d_name[len] != '.') ||
- (strtoll(dp->d_name + 1, NULL, 10) != 1)))) {
+ if ((dp->d_type != DT_REG) || (strncmp(dp->d_name, file, len) != 0) ||
+ (dp->d_name[len] && ((dp->d_name[len] != '.') ||
+ (strtoll(dp->d_name + 1, NULL, 10) != 1)))) {
continue;
}
@@ -547,7 +692,7 @@
bool found = false;
for (const auto& line : android::base::Split(file, "\n")) {
log_time t(log_time::EPOCH);
- char *ep = parseTime(t, line.c_str());
+ char* ep = parseTime(t, line.c_str());
if (!ep || (*ep != ' ')) {
continue;
}
@@ -581,22 +726,30 @@
return retval;
}
-} /* namespace android */
+const char* getenv(android_logcat_context_internal* context, const char* name) {
+ if (!context->envp || !name || !*name) return NULL;
-void reportErrorName(const char **current,
- const char* name,
+ for (size_t len = strlen(name), i = 0; context->envp[i]; ++i) {
+ if (strncmp(context->envp[i], name, len)) continue;
+ if (context->envp[i][len] == '=') return &context->envp[i][len + 1];
+ }
+ return NULL;
+}
+
+} // namespace android
+
+void reportErrorName(const char** current, const char* name,
bool blockSecurity) {
if (*current) {
- return;
+ return;
}
if (blockSecurity && (android_name_to_log_id(name) == LOG_ID_SECURITY)) {
- return;
+ return;
}
*current = name;
}
-int main(int argc, char **argv)
-{
+static int __logcat(android_logcat_context_internal* context) {
using namespace android;
int err;
int hasSetLogFormat = 0;
@@ -607,27 +760,99 @@
bool printStatistics = false;
bool printDividers = false;
unsigned long setLogSize = 0;
- char *setPruneList = NULL;
- char *setId = NULL;
+ char* setPruneList = NULL;
+ char* setId = NULL;
int mode = ANDROID_LOG_RDONLY;
- const char *forceFilters = NULL;
+ std::string forceFilters;
log_device_t* devices = NULL;
log_device_t* dev;
- struct logger_list *logger_list;
+ struct logger_list* logger_list;
size_t tail_lines = 0;
log_time tail_time(log_time::EPOCH);
size_t pid = 0;
bool got_t = false;
- signal(SIGPIPE, exit);
+ // object instantiations before goto's can happen
+ log_device_t unexpected("unexpected", false);
+ const char* openDeviceFail = NULL;
+ const char* clearFail = NULL;
+ const char* setSizeFail = NULL;
+ const char* getSizeFail = NULL;
+ int argc = context->argc;
+ char* const* argv = context->argv;
- g_logformat = android_log_format_new();
+ context->output = stdout;
+ context->error = stderr;
- if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
- show_help(argv[0]);
- return EXIT_SUCCESS;
+ for (int i = 0; i < argc; ++i) {
+ // Simulate shell stderr redirect parsing
+ if ((argv[i][0] != '2') || (argv[i][1] != '>')) continue;
+
+ size_t skip = (argv[i][2] == '>') + 2;
+ if (!strcmp(&argv[i][skip], "/dev/null")) {
+ context->stderr_null = true;
+ } else if (!strcmp(&argv[i][skip], "&1")) {
+ context->stderr_stdout = true;
+ } else {
+ // stderr file redirections are not supported
+ fprintf(context->stderr_stdout ? stdout : stderr,
+ "stderr redirection to file %s unsupported, skipping\n",
+ &argv[i][skip]);
+ }
}
+ // Deal with setting up file descriptors and FILE pointers
+ if (context->error_fd >= 0) {
+ if (context->error_fd == context->output_fd) {
+ context->stderr_stdout = true;
+ } else if (context->stderr_null) {
+ close(context->error_fd);
+ context->error_fd = -1;
+ } else {
+ context->error = fdopen(context->error_fd, "web");
+ if (!context->error) {
+ context->retval = -errno;
+ fprintf(context->stderr_stdout ? stdout : stderr,
+ "Failed to fdopen(error_fd=%d) %s\n", context->error_fd,
+ strerror(errno));
+ goto exit;
+ }
+ }
+ }
+ if (context->output_fd >= 0) {
+ context->output = fdopen(context->output_fd, "web");
+ if (!context->output) {
+ context->retval = -errno;
+ fprintf(context->stderr_stdout ? stdout : context->error,
+ "Failed to fdopen(output_fd=%d) %s\n", context->output_fd,
+ strerror(errno));
+ goto exit;
+ }
+ }
+ if (context->stderr_stdout) context->error = context->output;
+ if (context->stderr_null) {
+ context->error_fd = -1;
+ context->error = NULL;
+ }
+ // Only happens if output=stdout
+ if ((context->output_fd < 0) && context->output) {
+ context->output_fd = fileno(context->output);
+ }
+ // Only happens if error=stdout || error=stderr
+ if ((context->error_fd < 0) && context->error) {
+ context->error_fd = fileno(context->error);
+ }
+
+ context->logformat = android_log_format_new();
+
+ if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
+ show_help(context);
+ context->retval = EXIT_SUCCESS;
+ goto exit;
+ }
+
+ // danger: getopt is _not_ reentrant
+ optind = 1;
for (;;) {
int ret;
@@ -638,6 +863,7 @@
static const char id_str[] = "id";
static const char wrap_str[] = "wrap";
static const char print_str[] = "print";
+ // clang-format off
static const struct option long_options[] = {
{ "binary", no_argument, NULL, 'B' },
{ "buffer", required_argument, NULL, 'b' },
@@ -667,9 +893,11 @@
{ wrap_str, optional_argument, NULL, 0 },
{ NULL, 0, NULL, 0 }
};
+ // clang-format on
- ret = getopt_long(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:",
- long_options, &option_index);
+ ret = getopt_long(argc, argv,
+ ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", long_options,
+ &option_index);
if (ret < 0) {
break;
@@ -681,23 +909,25 @@
if (long_options[option_index].name == pid_str) {
// ToDo: determine runtime PID_MAX?
if (!getSizeTArg(optarg, &pid, 1)) {
- logcat_panic(HELP_TRUE, "%s %s out of range\n",
+ logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
long_options[option_index].name, optarg);
+ goto exit;
}
break;
}
if (long_options[option_index].name == wrap_str) {
- mode |= ANDROID_LOG_WRAP |
- ANDROID_LOG_RDONLY |
+ mode |= ANDROID_LOG_WRAP | ANDROID_LOG_RDONLY |
ANDROID_LOG_NONBLOCK;
// ToDo: implement API that supports setting a wrap timeout
size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
if (optarg && !getSizeTArg(optarg, &dummy, 1)) {
- logcat_panic(HELP_TRUE, "%s %s out of range\n",
+ logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
long_options[option_index].name, optarg);
+ goto exit;
}
- if (dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) {
- fprintf(stderr,
+ if ((dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) &&
+ context->error) {
+ fprintf(context->error,
"WARNING: %s %u seconds, ignoring %zu\n",
long_options[option_index].name,
ANDROID_LOG_WRAP_DEFAULT_TIMEOUT, dummy);
@@ -705,154 +935,163 @@
break;
}
if (long_options[option_index].name == print_str) {
- g_printItAnyways = true;
+ context->printItAnyways = true;
break;
}
if (long_options[option_index].name == debug_str) {
- g_debug = true;
+ context->debug = true;
break;
}
if (long_options[option_index].name == id_str) {
setId = optarg && optarg[0] ? optarg : NULL;
break;
}
- break;
+ break;
case 's':
// default to all silent
- android_log_addFilterRule(g_logformat, "*:s");
- break;
+ android_log_addFilterRule(context->logformat, "*:s");
+ break;
case 'c':
clearLog = true;
mode |= ANDROID_LOG_WRONLY;
- break;
+ break;
case 'L':
- mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK;
- break;
+ mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_PSTORE |
+ ANDROID_LOG_NONBLOCK;
+ break;
case 'd':
mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
- break;
+ break;
case 't':
got_t = true;
mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
- /* FALLTHRU */
+ // FALLTHRU
case 'T':
if (strspn(optarg, "0123456789") != strlen(optarg)) {
- char *cp = parseTime(tail_time, optarg);
+ char* cp = parseTime(tail_time, optarg);
if (!cp) {
- logcat_panic(HELP_FALSE,
- "-%c \"%s\" not in time format\n",
- ret, optarg);
+ logcat_panic(context, HELP_FALSE,
+ "-%c \"%s\" not in time format\n", ret,
+ optarg);
+ goto exit;
}
if (*cp) {
char c = *cp;
*cp = '\0';
- fprintf(stderr,
+ if (context->error) {
+ fprintf(
+ context->error,
"WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
ret, optarg, c, cp + 1);
+ }
*cp = c;
}
} else {
if (!getSizeTArg(optarg, &tail_lines, 1)) {
- fprintf(stderr,
- "WARNING: -%c %s invalid, setting to 1\n",
- ret, optarg);
+ if (context->error) {
+ fprintf(context->error,
+ "WARNING: -%c %s invalid, setting to 1\n",
+ ret, optarg);
+ }
tail_lines = 1;
}
}
- break;
+ break;
case 'D':
printDividers = true;
- break;
+ break;
case 'e':
- g_regex = new pcrecpp::RE(optarg);
- break;
+ context->regex = new pcrecpp::RE(optarg);
+ break;
case 'm': {
- char *end = NULL;
- if (!getSizeTArg(optarg, &g_maxCount)) {
- logcat_panic(HELP_FALSE, "-%c \"%s\" isn't an "
- "integer greater than zero\n", ret, optarg);
+ char* end = NULL;
+ if (!getSizeTArg(optarg, &context->maxCount)) {
+ logcat_panic(context, HELP_FALSE,
+ "-%c \"%s\" isn't an "
+ "integer greater than zero\n",
+ ret, optarg);
+ goto exit;
}
- }
- break;
+ } break;
case 'g':
if (!optarg) {
getLogSize = true;
break;
}
- // FALLTHRU
+ // FALLTHRU
case 'G': {
- char *cp;
+ char* cp;
if (strtoll(optarg, &cp, 0) > 0) {
setLogSize = strtoll(optarg, &cp, 0);
} else {
setLogSize = 0;
}
- switch(*cp) {
- case 'g':
- case 'G':
- setLogSize *= 1024;
- /* FALLTHRU */
- case 'm':
- case 'M':
- setLogSize *= 1024;
- /* FALLTHRU */
- case 'k':
- case 'K':
- setLogSize *= 1024;
- /* FALLTHRU */
- case '\0':
- break;
+ switch (*cp) {
+ case 'g':
+ case 'G':
+ setLogSize *= 1024;
+ // FALLTHRU
+ case 'm':
+ case 'M':
+ setLogSize *= 1024;
+ // FALLTHRU
+ case 'k':
+ case 'K':
+ setLogSize *= 1024;
+ // FALLTHRU
+ case '\0':
+ break;
- default:
- setLogSize = 0;
+ default:
+ setLogSize = 0;
}
if (!setLogSize) {
- fprintf(stderr, "ERROR: -G <num><multiplier>\n");
- return EXIT_FAILURE;
+ logcat_panic(context, HELP_FALSE,
+ "ERROR: -G <num><multiplier>\n");
+ goto exit;
}
- }
- break;
+ } break;
case 'p':
if (!optarg) {
getPruneList = true;
break;
}
- // FALLTHRU
+ // FALLTHRU
case 'P':
setPruneList = optarg;
- break;
+ break;
case 'b': {
unsigned idMask = 0;
while ((optarg = strtok(optarg, ",:; \t\n\r\f")) != NULL) {
if (strcmp(optarg, "default") == 0) {
- idMask |= (1 << LOG_ID_MAIN) |
- (1 << LOG_ID_SYSTEM) |
+ idMask |= (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) |
(1 << LOG_ID_CRASH);
} else if (strcmp(optarg, "all") == 0) {
allSelected = true;
idMask = (unsigned)-1;
} else {
log_id_t log_id = android_name_to_log_id(optarg);
- const char *name = android_log_id_to_name(log_id);
+ const char* name = android_log_id_to_name(log_id);
if (strcmp(name, optarg) != 0) {
- logcat_panic(HELP_TRUE,
+ logcat_panic(context, HELP_TRUE,
"unknown buffer %s\n", optarg);
+ goto exit;
}
if (log_id == LOG_ID_SECURITY) allSelected = false;
idMask |= (1 << log_id);
@@ -861,7 +1100,7 @@
}
for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
- const char *name = android_log_id_to_name((log_id_t)i);
+ const char* name = android_log_id_to_name((log_id_t)i);
log_id_t log_id = android_name_to_log_id(name);
if (log_id != (log_id_t)i) {
@@ -885,8 +1124,8 @@
continue;
}
- bool binary = !strcmp(name, "events") ||
- !strcmp(name, "security");
+ bool binary =
+ !strcmp(name, "events") || !strcmp(name, "security");
log_device_t* d = new log_device_t(name, binary);
if (dev) {
@@ -895,115 +1134,108 @@
} else {
devices = dev = d;
}
- g_devCount++;
+ context->devCount++;
}
- }
- break;
+ } break;
case 'B':
- g_printBinary = 1;
- break;
+ context->printBinary = 1;
+ break;
case 'f':
if ((tail_time == log_time::EPOCH) && (tail_lines == 0)) {
tail_time = lastLogTime(optarg);
}
// redirect output to a file
- g_outputFileName = optarg;
- break;
+ context->outputFileName = optarg;
+ break;
case 'r':
- if (!getSizeTArg(optarg, &g_logRotateSizeKBytes, 1)) {
- logcat_panic(HELP_TRUE,
+ if (!getSizeTArg(optarg, &context->logRotateSizeKBytes, 1)) {
+ logcat_panic(context, HELP_TRUE,
"Invalid parameter \"%s\" to -r\n", optarg);
+ goto exit;
}
- break;
+ break;
case 'n':
- if (!getSizeTArg(optarg, &g_maxRotatedLogs, 1)) {
- logcat_panic(HELP_TRUE,
+ if (!getSizeTArg(optarg, &context->maxRotatedLogs, 1)) {
+ logcat_panic(context, HELP_TRUE,
"Invalid parameter \"%s\" to -n\n", optarg);
+ goto exit;
}
- break;
+ break;
case 'v':
if (!strcmp(optarg, "help") || !strcmp(optarg, "--help")) {
- show_format_help();
- exit(0);
+ show_format_help(context);
+ context->retval = EXIT_SUCCESS;
+ goto exit;
}
- err = setLogFormat(optarg);
+ err = setLogFormat(context, optarg);
if (err < 0) {
- logcat_panic(HELP_FORMAT,
+ logcat_panic(context, HELP_FORMAT,
"Invalid parameter \"%s\" to -v\n", optarg);
+ goto exit;
}
hasSetLogFormat |= err;
- break;
+ break;
case 'Q':
- /* this is a *hidden* option used to start a version of logcat */
- /* in an emulated device only. it basically looks for androidboot.logcat= */
- /* on the kernel command line. If something is found, it extracts a log filter */
- /* and uses it to run the program. If nothing is found, the program should */
- /* quit immediately */
-#define KERNEL_OPTION "androidboot.logcat="
-#define CONSOLE_OPTION "androidboot.console="
+#define KERNEL_OPTION "androidboot.logcat="
+#define CONSOLE_OPTION "androidboot.console="
+ // This is a *hidden* option used to start a version of logcat
+ // in an emulated device only. It basically looks for
+ // androidboot.logcat= on the kernel command line. If
+ // something is found, it extracts a log filter and uses it to
+ // run the program. If nothing is found, the program should
+ // quit immediately.
{
- int fd;
- char* logcat;
- char* console;
- int force_exit = 1;
- static char cmdline[1024];
+ std::string cmdline;
+ android::base::ReadFileToString("/proc/cmdline", &cmdline);
- fd = open("/proc/cmdline", O_RDONLY);
- if (fd >= 0) {
- int n = read(fd, cmdline, sizeof(cmdline)-1 );
- if (n < 0) n = 0;
- cmdline[n] = 0;
- close(fd);
- } else {
- cmdline[0] = 0;
+ const char* logcat = strstr(cmdline.c_str(), KERNEL_OPTION);
+ // if nothing found or invalid filters, exit quietly
+ if (!logcat) {
+ context->retval = EXIT_SUCCESS;
+ goto exit;
}
- logcat = strstr( cmdline, KERNEL_OPTION );
- console = strstr( cmdline, CONSOLE_OPTION );
- if (logcat != NULL) {
- char* p = logcat + sizeof(KERNEL_OPTION)-1;;
- char* q = strpbrk( p, " \t\n\r" );;
+ const char* p = logcat + strlen(KERNEL_OPTION);
+ const char* q = strpbrk(p, " \t\n\r");
+ if (!q) q = p + strlen(p);
+ forceFilters = std::string(p, q);
- if (q != NULL)
- *q = 0;
+ // redirect our output to the emulator console
+ const char* console =
+ strstr(cmdline.c_str(), CONSOLE_OPTION);
+ if (!console) break;
- forceFilters = p;
- force_exit = 0;
- }
- /* if nothing found or invalid filters, exit quietly */
- if (force_exit) {
- return EXIT_SUCCESS;
+ p = console + strlen(CONSOLE_OPTION);
+ q = strpbrk(p, " \t\n\r");
+ int len = q ? q - p : strlen(p);
+ std::string devname = "/dev/" + std::string(p, len);
+ cmdline.erase();
+
+ if (context->error) {
+ fprintf(context->error, "logcat using %s\n",
+ devname.c_str());
}
- /* redirect our output to the emulator console */
- if (console) {
- char* p = console + sizeof(CONSOLE_OPTION)-1;
- char* q = strpbrk( p, " \t\n\r" );
- char devname[64];
- int len;
+ FILE* fp = fopen(devname.c_str(), "web");
+ devname.erase();
+ if (!fp) break;
- if (q != NULL) {
- len = q - p;
- } else
- len = strlen(p);
-
- len = snprintf( devname, sizeof(devname), "/dev/%.*s", len, p );
- fprintf(stderr, "logcat using %s (%d)\n", devname, len);
- if (len < (int)sizeof(devname)) {
- fd = open( devname, O_WRONLY );
- if (fd >= 0) {
- dup2(fd, 1);
- dup2(fd, 2);
- close(fd);
- }
- }
- }
+ // close output and error channels, replace with console
+ android::close_output(context);
+ android::close_error(context);
+ context->stderr_stdout = true;
+ context->output = fp;
+ context->output_fd = fileno(fp);
+ if (context->stderr_null) break;
+ context->stderr_stdout = true;
+ context->error = fp;
+ context->error_fd = fileno(fp);
}
break;
@@ -1012,103 +1244,119 @@
break;
case ':':
- logcat_panic(HELP_TRUE,
+ logcat_panic(context, HELP_TRUE,
"Option -%c needs an argument\n", optopt);
- break;
+ goto exit;
default:
- logcat_panic(HELP_TRUE,
- "Unrecognized Option %c\n", optopt);
- break;
+ logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n",
+ optopt);
+ goto exit;
}
}
- if (g_maxCount && got_t) {
- logcat_panic(HELP_TRUE,
+ if (context->maxCount && got_t) {
+ logcat_panic(context, HELP_TRUE,
"Cannot use -m (--max-count) and -t together\n");
+ goto exit;
}
- if (g_printItAnyways && (!g_regex || !g_maxCount)) {
+ if (context->printItAnyways && (!context->regex || !context->maxCount)) {
// One day it would be nice if --print -v color and --regex <expr>
// could play with each other and show regex highlighted content.
- fprintf(stderr, "WARNING: "
+ // clang-format off
+ if (context->error) {
+ fprintf(context->error, "WARNING: "
"--print ignored, to be used in combination with\n"
- " "
+ " "
"--regex <expr> and --max-count <N>\n");
- g_printItAnyways = false;
+ }
+ context->printItAnyways = false;
}
if (!devices) {
dev = devices = new log_device_t("main", false);
- g_devCount = 1;
+ context->devCount = 1;
if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
dev = dev->next = new log_device_t("system", false);
- g_devCount++;
+ context->devCount++;
}
if (android_name_to_log_id("crash") == LOG_ID_CRASH) {
dev = dev->next = new log_device_t("crash", false);
- g_devCount++;
+ context->devCount++;
}
}
- if (g_logRotateSizeKBytes != 0 && g_outputFileName == NULL) {
- logcat_panic(HELP_TRUE, "-r requires -f as well\n");
+ if (context->logRotateSizeKBytes != 0 && context->outputFileName == NULL) {
+ logcat_panic(context, HELP_TRUE, "-r requires -f as well\n");
+ goto exit;
}
if (setId != NULL) {
- if (g_outputFileName == NULL) {
- logcat_panic(HELP_TRUE, "--id='%s' requires -f as well\n", setId);
+ if (context->outputFileName == NULL) {
+ logcat_panic(context, HELP_TRUE,
+ "--id='%s' requires -f as well\n", setId);
+ goto exit;
}
- std::string file_name = android::base::StringPrintf("%s.id", g_outputFileName);
+ std::string file_name = android::base::StringPrintf(
+ "%s.id", context->outputFileName);
std::string file;
bool file_ok = android::base::ReadFileToString(file_name, &file);
- android::base::WriteStringToFile(setId, file_name,
- S_IRUSR | S_IWUSR, getuid(), getgid());
+ android::base::WriteStringToFile(setId, file_name, S_IRUSR | S_IWUSR,
+ getuid(), getgid());
if (!file_ok || (file.compare(setId) == 0)) {
setId = NULL;
}
}
if (hasSetLogFormat == 0) {
- const char* logFormat = getenv("ANDROID_PRINTF_LOG");
+ const char* logFormat = android::getenv(context, "ANDROID_PRINTF_LOG");
if (logFormat != NULL) {
- err = setLogFormat(logFormat);
- if (err < 0) {
- fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n",
- logFormat);
+ err = setLogFormat(context, logFormat);
+ if ((err < 0) && context->error) {
+ fprintf(context->error,
+ "invalid format in ANDROID_PRINTF_LOG '%s'\n",
+ logFormat);
}
} else {
- setLogFormat("threadtime");
+ setLogFormat(context, "threadtime");
}
}
- if (forceFilters) {
- err = android_log_addFilterString(g_logformat, forceFilters);
+ if (forceFilters.size()) {
+ err = android_log_addFilterString(context->logformat,
+ forceFilters.c_str());
if (err < 0) {
- logcat_panic(HELP_FALSE,
+ logcat_panic(context, HELP_FALSE,
"Invalid filter expression in logcat args\n");
+ goto exit;
}
} else if (argc == optind) {
// Add from environment variable
- char *env_tags_orig = getenv("ANDROID_LOG_TAGS");
+ const char* env_tags_orig = android::getenv(context, "ANDROID_LOG_TAGS");
if (env_tags_orig != NULL) {
- err = android_log_addFilterString(g_logformat, env_tags_orig);
+ err = android_log_addFilterString(context->logformat,
+ env_tags_orig);
if (err < 0) {
- logcat_panic(HELP_TRUE,
+ logcat_panic(context, HELP_TRUE,
"Invalid filter expression in ANDROID_LOG_TAGS\n");
+ goto exit;
}
}
} else {
// Add from commandline
for (int i = optind ; i < argc ; i++) {
- err = android_log_addFilterString(g_logformat, argv[i]);
+ // skip stderr redirections of _all_ kinds
+ if ((argv[i][0] == '2') && (argv[i][1] == '>')) continue;
+ err = android_log_addFilterString(context->logformat, argv[i]);
if (err < 0) {
- logcat_panic(HELP_TRUE,
+ logcat_panic(context, HELP_TRUE,
"Invalid filter expression '%s'\n", argv[i]);
+ goto exit;
}
}
}
@@ -1119,10 +1367,6 @@
} else {
logger_list = android_logger_list_alloc(mode, tail_lines, pid);
}
- const char *openDeviceFail = NULL;
- const char *clearFail = NULL;
- const char *setSizeFail = NULL;
- const char *getSizeFail = NULL;
// We have three orthogonal actions below to clear, set log size and
// get log size. All sharing the same iteration loop.
while (dev) {
@@ -1136,18 +1380,21 @@
}
if (clearLog || setId) {
- if (g_outputFileName) {
+ if (context->outputFileName) {
int maxRotationCountDigits =
- (g_maxRotatedLogs > 0) ? (int) (floor(log10(g_maxRotatedLogs) + 1)) : 0;
+ (context->maxRotatedLogs > 0) ?
+ (int)(floor(log10(context->maxRotatedLogs) + 1)) :
+ 0;
- for (int i = g_maxRotatedLogs ; i >= 0 ; --i) {
+ for (int i = context->maxRotatedLogs ; i >= 0 ; --i) {
std::string file;
if (i == 0) {
- file = android::base::StringPrintf("%s", g_outputFileName);
+ file = android::base::StringPrintf(
+ "%s", context->outputFileName);
} else {
file = android::base::StringPrintf("%s.%.*d",
- g_outputFileName, maxRotationCountDigits, i);
+ context->outputFileName, maxRotationCountDigits, i);
}
if (file.length() == 0) {
@@ -1181,66 +1428,79 @@
if ((size < 0) || (readable < 0)) {
reportErrorName(&getSizeFail, dev->device, allSelected);
} else {
- printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
- "max entry is %db, max payload is %db\n", dev->device,
+ std::string str = android::base::StringPrintf(
+ "%s: ring buffer is %ld%sb (%ld%sb consumed),"
+ " max entry is %db, max payload is %db\n",
+ dev->device,
value_of_size(size), multiplier_of_size(size),
value_of_size(readable), multiplier_of_size(readable),
- (int) LOGGER_ENTRY_MAX_LEN,
- (int) LOGGER_ENTRY_MAX_PAYLOAD);
+ (int)LOGGER_ENTRY_MAX_LEN,
+ (int)LOGGER_ENTRY_MAX_PAYLOAD);
+ TEMP_FAILURE_RETRY(write(context->output_fd,
+ str.data(), str.length()));
}
}
dev = dev->next;
}
+
+ context->retval = EXIT_SUCCESS;
+
// report any errors in the above loop and exit
if (openDeviceFail) {
- logcat_panic(HELP_FALSE,
+ logcat_panic(context, HELP_FALSE,
"Unable to open log device '%s'\n", openDeviceFail);
+ goto close;
}
if (clearFail) {
- logcat_panic(HELP_FALSE,
+ logcat_panic(context, HELP_FALSE,
"failed to clear the '%s' log\n", clearFail);
+ goto close;
}
if (setSizeFail) {
- logcat_panic(HELP_FALSE,
+ logcat_panic(context, HELP_FALSE,
"failed to set the '%s' log size\n", setSizeFail);
+ goto close;
}
if (getSizeFail) {
- logcat_panic(HELP_FALSE,
+ logcat_panic(context, HELP_FALSE,
"failed to get the readable '%s' log size", getSizeFail);
+ goto close;
}
if (setPruneList) {
size_t len = strlen(setPruneList);
- /*extra 32 bytes are needed by android_logger_set_prune_list */
+ // extra 32 bytes are needed by android_logger_set_prune_list
size_t bLen = len + 32;
- char *buf = NULL;
+ char* buf = NULL;
if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
buf[len] = '\0';
if (android_logger_set_prune_list(logger_list, buf, bLen)) {
- logcat_panic(HELP_FALSE, "failed to set the prune list");
+ logcat_panic(context, HELP_FALSE,
+ "failed to set the prune list");
}
free(buf);
} else {
- logcat_panic(HELP_FALSE, "failed to set the prune list (alloc)");
+ logcat_panic(context, HELP_FALSE,
+ "failed to set the prune list (alloc)");
}
+ goto close;
}
if (printStatistics || getPruneList) {
size_t len = 8192;
- char *buf;
+ char* buf;
- for (int retry = 32;
- (retry >= 0) && ((buf = new char [len]));
- delete [] buf, buf = NULL, --retry) {
+ for (int retry = 32; (retry >= 0) && ((buf = new char[len]));
+ delete[] buf, buf = NULL, --retry) {
if (getPruneList) {
android_logger_get_prune_list(logger_list, buf, len);
} else {
android_logger_get_statistics(logger_list, buf, len);
}
- buf[len-1] = '\0';
+ buf[len - 1] = '\0';
if (atol(buf) < 3) {
- delete [] buf;
+ delete[] buf;
buf = NULL;
break;
}
@@ -1253,11 +1513,12 @@
}
if (!buf) {
- logcat_panic(HELP_FALSE, "failed to read data");
+ logcat_panic(context, HELP_FALSE, "failed to read data");
+ goto close;
}
// remove trailing FF
- char *cp = buf + len - 1;
+ char* cp = buf + len - 1;
*cp = '\0';
bool truncated = *--cp != '\f';
if (!truncated) {
@@ -1275,37 +1536,32 @@
}
}
- printf("%s", cp);
- delete [] buf;
- return EXIT_SUCCESS;
+ len = strlen(cp);
+ TEMP_FAILURE_RETRY(write(context->output_fd, cp, len));
+ delete[] buf;
+ goto close;
}
- if (getLogSize) {
- return EXIT_SUCCESS;
- }
- if (setLogSize || setPruneList) {
- return EXIT_SUCCESS;
- }
- if (clearLog) {
- return EXIT_SUCCESS;
+ if (getLogSize || setLogSize || clearLog) {
+ goto close;
}
- setupOutputAndSchedulingPolicy((mode & ANDROID_LOG_NONBLOCK) == 0);
+ setupOutputAndSchedulingPolicy(context, (mode & ANDROID_LOG_NONBLOCK) == 0);
+ if (context->stop) goto close;
- //LOG_EVENT_INT(10, 12345);
- //LOG_EVENT_LONG(11, 0x1122334455667788LL);
- //LOG_EVENT_STRING(0, "whassup, doc?");
+ // LOG_EVENT_INT(10, 12345);
+ // LOG_EVENT_LONG(11, 0x1122334455667788LL);
+ // LOG_EVENT_STRING(0, "whassup, doc?");
dev = NULL;
- log_device_t unexpected("unexpected", false);
- while (!g_maxCount || (g_printCount < g_maxCount)) {
+ while (!context->stop &&
+ (!context->maxCount || (context->printCount < context->maxCount))) {
struct log_msg log_msg;
- log_device_t* d;
int ret = android_logger_list_read(logger_list, &log_msg);
-
if (ret == 0) {
- logcat_panic(HELP_FALSE, "read: unexpected EOF!\n");
+ logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
+ break;
}
if (ret < 0) {
@@ -1314,37 +1570,217 @@
}
if (ret == -EIO) {
- logcat_panic(HELP_FALSE, "read: unexpected EOF!\n");
+ logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
+ break;
}
if (ret == -EINVAL) {
- logcat_panic(HELP_FALSE, "read: unexpected length.\n");
+ logcat_panic(context, HELP_FALSE, "read: unexpected length.\n");
+ break;
}
- logcat_panic(HELP_FALSE, "logcat read failure");
+ logcat_panic(context, HELP_FALSE, "logcat read failure");
+ break;
}
+ log_device_t* d;
for (d = devices; d; d = d->next) {
if (android_name_to_log_id(d->device) == log_msg.id()) {
break;
}
}
if (!d) {
- g_devCount = 2; // set to Multiple
+ context->devCount = 2; // set to Multiple
d = &unexpected;
d->binary = log_msg.id() == LOG_ID_EVENTS;
}
if (dev != d) {
dev = d;
- maybePrintStart(dev, printDividers);
+ maybePrintStart(context, dev, printDividers);
+ if (context->stop) break;
}
- if (g_printBinary) {
- printBinary(&log_msg);
+ if (context->printBinary) {
+ printBinary(context, &log_msg);
} else {
- processBuffer(dev, &log_msg);
+ processBuffer(context, dev, &log_msg);
}
}
+close:
android_logger_list_free(logger_list);
- return EXIT_SUCCESS;
+exit:
+ // close write end of pipe to help things along
+ if (context->output_fd == context->fds[1]) {
+ android::close_output(context);
+ }
+ if (context->error_fd == context->fds[1]) {
+ android::close_error(context);
+ }
+ if (context->fds[1] >= 0) {
+ // NB: should be closed by the above
+ int save_errno = errno;
+ close(context->fds[1]);
+ errno = save_errno;
+ context->fds[1] = -1;
+ }
+ context->thread_stopped = true;
+ return context->retval;
+}
+
+// Can block
+int android_logcat_run_command(android_logcat_context ctx,
+ int output, int error,
+ int argc, char* const* argv,
+ char* const* envp) {
+ android_logcat_context_internal* context = ctx;
+
+ context->output_fd = output;
+ context->error_fd = error;
+ context->argc = argc;
+ context->argv = argv;
+ context->envp = envp;
+ context->stop = false;
+ context->thread_stopped = false;
+ return __logcat(context);
+}
+
+// starts a thread, opens a pipe, returns reading end.
+int android_logcat_run_command_thread(android_logcat_context ctx,
+ int argc, char* const* argv,
+ char* const* envp) {
+ android_logcat_context_internal* context = ctx;
+
+ int save_errno = EBUSY;
+ if ((context->fds[0] >= 0) || (context->fds[1] >= 0)) {
+ goto exit;
+ }
+
+ if (pipe(context->fds) < 0) {
+ save_errno = errno;
+ goto exit;
+ }
+
+ pthread_attr_t attr;
+ if (pthread_attr_init(&attr)) {
+ save_errno = errno;
+ goto close_exit;
+ }
+
+ struct sched_param param;
+ memset(¶m, 0, sizeof(param));
+ pthread_attr_setschedparam(&attr, ¶m);
+ pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
+ if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
+ int save_errno = errno;
+ goto pthread_attr_exit;
+ }
+
+ context->stop = false;
+ context->thread_stopped = false;
+ context->output_fd = context->fds[1];
+ // save off arguments so they remain while thread is active.
+ for (int i = 0; i < argc; ++i) {
+ context->args.push_back(std::string(argv[i]));
+ }
+ // save off environment so they remain while thread is active.
+ if (envp) for (size_t i = 0; envp[i]; ++i) {
+ context->envs.push_back(std::string(envp[i]));
+ }
+
+ for (auto& str : context->args) {
+ context->argv_hold.push_back(str.c_str());
+ }
+ context->argv_hold.push_back(NULL);
+ for (auto& str : context->envs) {
+ context->envp_hold.push_back(str.c_str());
+ }
+ context->envp_hold.push_back(NULL);
+
+ context->argc = context->argv_hold.size() - 1;
+ context->argv = (char* const*)&context->argv_hold[0];
+ context->envp = (char* const*)&context->envp_hold[0];
+
+#ifdef DEBUG
+ fprintf(stderr, "argv[%d] = {", context->argc);
+ for (auto str : context->argv_hold) {
+ fprintf(stderr, " \"%s\"", str ?: "NULL");
+ }
+ fprintf(stderr, " }\n");
+ fflush(stderr);
+#endif
+ context->retval = EXIT_SUCCESS;
+ if (pthread_create(&context->thr, &attr,
+ (void*(*)(void*))__logcat, context)) {
+ int save_errno = errno;
+ goto argv_exit;
+ }
+ pthread_attr_destroy(&attr);
+
+ return context->fds[0];
+
+argv_exit:
+ context->argv_hold.clear();
+ context->args.clear();
+ context->envp_hold.clear();
+ context->envs.clear();
+pthread_attr_exit:
+ pthread_attr_destroy(&attr);
+close_exit:
+ close(context->fds[0]);
+ context->fds[0] = -1;
+ close(context->fds[1]);
+ context->fds[1] = -1;
+exit:
+ errno = save_errno;
+ context->stop = true;
+ context->thread_stopped = true;
+ context->retval = EXIT_FAILURE;
+ return -1;
+}
+
+// test if the thread is still doing 'stuff'
+int android_logcat_run_command_thread_running(android_logcat_context ctx) {
+ android_logcat_context_internal* context = ctx;
+
+ return context->thread_stopped == false;
+}
+
+// Finished with context
+int android_logcat_destroy(android_logcat_context* ctx) {
+ android_logcat_context_internal* context = *ctx;
+
+ *ctx = NULL;
+
+ context->stop = true;
+
+ while (context->thread_stopped == false) {
+ sched_yield();
+ }
+
+ delete context->regex;
+ context->argv_hold.clear();
+ context->args.clear();
+ context->envp_hold.clear();
+ context->envs.clear();
+ if (context->fds[0] >= 0) {
+ close(context->fds[0]);
+ context->fds[0] = -1;
+ }
+ android::close_output(context);
+ android::close_error(context);
+ if (context->fds[1] >= 0) {
+ // NB: could be closed by the above fclose(s), ignore error.
+ int save_errno = errno;
+ close(context->fds[1]);
+ errno = save_errno;
+ context->fds[1] = -1;
+ }
+
+ android_closeEventTagMap(context->eventTagMap);
+
+ int retval = context->retval;
+
+ free(context);
+
+ return retval;
}
diff --git a/logcat/logcat_main.cpp b/logcat/logcat_main.cpp
new file mode 100644
index 0000000..9477e79
--- /dev/null
+++ b/logcat/logcat_main.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <signal.h>
+#include <stdlib.h>
+
+#include <log/logcat.h>
+
+int main(int argc, char** argv, char** envp) {
+ android_logcat_context ctx = create_android_logcat();
+ if (!ctx) return -1;
+ signal(SIGPIPE, exit);
+ int retval = android_logcat_run_command(ctx, -1, -1, argc, argv, envp);
+ int ret = android_logcat_destroy(&ctx);
+ if (!ret) ret = retval;
+ return ret;
+}
diff --git a/logcat/logcat_system.cpp b/logcat/logcat_system.cpp
new file mode 100644
index 0000000..ea393bd
--- /dev/null
+++ b/logcat/logcat_system.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <log/logcat.h>
+
+static std::string unquote(const char*& cp, const char*& delim) {
+ if ((*cp == '\'') || (*cp == '"')) {
+ // KISS: Simple quotes. Do not handle the case
+ // of concatenation like "blah"foo'bar'
+ char quote = *cp++;
+ delim = strchr(cp, quote);
+ if (!delim) delim = cp + strlen(cp);
+ std::string str(cp, delim);
+ if (*delim) ++delim;
+ return str;
+ }
+ delim = strpbrk(cp, " \t\f\r\n");
+ if (!delim) delim = cp + strlen(cp);
+ return std::string(cp, delim);
+}
+
+static bool __android_logcat_parse(const char* command,
+ std::vector<std::string>& args,
+ std::vector<std::string>& envs) {
+ for (const char *delim, *cp = command; cp && *cp; cp = delim) {
+ while (isspace(*cp)) ++cp;
+ if ((args.size() == 0) && (*cp != '=') && !isdigit(*cp)) {
+ const char* env = cp;
+ while (isalnum(*cp) || (*cp == '_')) ++cp;
+ if (cp && (*cp == '=')) {
+ std::string str(env, ++cp);
+ str += unquote(cp, delim);
+ envs.push_back(str);
+ continue;
+ }
+ cp = env;
+ }
+ args.push_back(unquote(cp, delim));
+ if ((args.size() == 1) && (args[0] != "logcat") &&
+ (args[0] != "/system/bin/logcat")) {
+ return false;
+ }
+ }
+ return args.size() != 0;
+}
+
+FILE* android_logcat_popen(android_logcat_context* ctx, const char* command) {
+ *ctx = NULL;
+
+ std::vector<std::string> args;
+ std::vector<std::string> envs;
+ if (!__android_logcat_parse(command, args, envs)) return NULL;
+
+ std::vector<const char*> argv;
+ for (auto& str : args) {
+ argv.push_back(str.c_str());
+ }
+ argv.push_back(NULL);
+
+ std::vector<const char*> envp;
+ for (auto& str : envs) {
+ envp.push_back(str.c_str());
+ }
+ envp.push_back(NULL);
+
+ *ctx = create_android_logcat();
+ if (!*ctx) return NULL;
+
+ int fd = android_logcat_run_command_thread(
+ *ctx, argv.size() - 1, (char* const*)&argv[0], (char* const*)&envp[0]);
+ argv.clear();
+ args.clear();
+ envp.clear();
+ envs.clear();
+ if (fd < 0) {
+ android_logcat_destroy(ctx);
+ return NULL;
+ }
+
+ FILE* retval = fdopen(fd, "reb");
+ if (!retval) android_logcat_destroy(ctx);
+ return retval;
+}
+
+int android_logcat_pclose(android_logcat_context* ctx, FILE* output) {
+ if (*ctx) {
+ static const useconds_t wait_sample = 20000;
+ // Wait two seconds maximum
+ for (size_t retry = ((2 * 1000000) + wait_sample - 1) / wait_sample;
+ android_logcat_run_command_thread_running(*ctx) && retry; --retry) {
+ usleep(wait_sample);
+ }
+ }
+
+ if (output) fclose(output);
+ return android_logcat_destroy(ctx);
+}
+
+int android_logcat_system(const char* command) {
+ std::vector<std::string> args;
+ std::vector<std::string> envs;
+ if (!__android_logcat_parse(command, args, envs)) return -1;
+
+ std::vector<const char*> argv;
+ for (auto& str : args) {
+ argv.push_back(str.c_str());
+ }
+ argv.push_back(NULL);
+
+ std::vector<const char*> envp;
+ for (auto& str : envs) {
+ envp.push_back(str.c_str());
+ }
+ envp.push_back(NULL);
+
+ android_logcat_context ctx = create_android_logcat();
+ if (!ctx) return -1;
+ /* Command return value */
+ int retval = android_logcat_run_command(ctx, -1, -1, argv.size() - 1,
+ (char* const*)&argv[0],
+ (char* const*)&envp[0]);
+ /* destroy return value */
+ int ret = android_logcat_destroy(&ctx);
+ /* Paranoia merging any discrepancies between the two return values */
+ if (!ret) ret = retval;
+ return ret;
+}
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index cb8b061..1c3feba 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -48,6 +48,7 @@
test_src_files := \
logcat_test.cpp \
+ liblogcat_test.cpp \
# Build tests for the device (with .so). Run with:
# adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests
@@ -55,6 +56,6 @@
LOCAL_MODULE := $(test_module_prefix)unit-tests
LOCAL_MODULE_TAGS := $(test_tags)
LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog libbase
+LOCAL_SHARED_LIBRARIES := liblog libbase liblogcat
LOCAL_SRC_FILES := $(test_src_files)
include $(BUILD_NATIVE_TEST)
diff --git a/logcat/tests/liblogcat_test.cpp b/logcat/tests/liblogcat_test.cpp
new file mode 100644
index 0000000..9e9a2c2
--- /dev/null
+++ b/logcat/tests/liblogcat_test.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <log/logcat.h>
+
+#define logcat_define(context) android_logcat_context context
+#define logcat_popen(context, command) android_logcat_popen(&context, command)
+#define logcat_pclose(context, fp) android_logcat_pclose(&context, fp)
+#define logcat_system(command) android_logcat_system(command)
+#define logcat liblogcat
+
+#include "logcat_test.cpp"
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 081bf92..9c777b3 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -33,36 +33,45 @@
#include <log/log.h>
#include <log/log_event_list.h>
+#ifndef logcat_popen
+#define logcat_define(context)
+#define logcat_popen(context, command) popen((command), "r")
+#define logcat_pclose(context, fp) pclose(fp)
+#define logcat_system(command) system(command)
+#endif
+
#define BIG_BUFFER (5 * 1024)
// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
// non-syscall libs. Since we are only using this in the emergency of
// a signal to stuff a terminating code into the logs, we will spin rather
// than try a usleep.
-#define LOG_FAILURE_RETRY(exp) ({ \
- typeof (exp) _rc; \
- do { \
- _rc = (exp); \
- } while (((_rc == -1) \
- && ((errno == EINTR) \
- || (errno == EAGAIN))) \
- || (_rc == -EINTR) \
- || (_rc == -EAGAIN)); \
- _rc; })
+#define LOG_FAILURE_RETRY(exp) \
+ ({ \
+ typeof(exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (((_rc == -1) && ((errno == EINTR) || (errno == EAGAIN))) || \
+ (_rc == -EINTR) || (_rc == -EAGAIN)); \
+ _rc; \
+ })
static const char begin[] = "--------- beginning of ";
TEST(logcat, buckets) {
- FILE *fp;
+ FILE* fp;
+ logcat_define(ctx);
#undef LOG_TAG
#define LOG_TAG "inject"
RLOGE("logcat.buckets");
sleep(1);
- ASSERT_TRUE(NULL != (fp = popen(
- "logcat -b radio -b events -b system -b main -d 2>/dev/null",
- "r")));
+ ASSERT_TRUE(
+ NULL !=
+ (fp = logcat_popen(
+ ctx,
+ "logcat -b radio -b events -b system -b main -d 2>/dev/null")));
char buffer[BIG_BUFFER];
@@ -71,7 +80,7 @@
while (fgets(buffer, sizeof(buffer), fp)) {
if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
- while (char *cp = strrchr(buffer, '\n')) {
+ while (char* cp = strrchr(buffer, '\n')) {
*cp = '\0';
}
log_id_t id = android_name_to_log_id(buffer + sizeof(begin) - 1);
@@ -80,7 +89,7 @@
}
}
- pclose(fp);
+ logcat_pclose(ctx, fp);
EXPECT_EQ(15, ids);
@@ -88,11 +97,14 @@
}
TEST(logcat, event_tag_filter) {
- FILE *fp;
+ FILE* fp;
+ logcat_define(ctx);
- ASSERT_TRUE(NULL != (fp = popen(
- "logcat -b events -d -s auditd am_proc_start am_pss am_proc_bound dvm_lock_sample am_wtf 2>/dev/null",
- "r")));
+ ASSERT_TRUE(NULL !=
+ (fp = logcat_popen(ctx,
+ "logcat -b events -d -s auditd "
+ "am_proc_start am_pss am_proc_bound "
+ "dvm_lock_sample am_wtf 2>/dev/null")));
char buffer[BIG_BUFFER];
@@ -102,7 +114,7 @@
++count;
}
- pclose(fp);
+ logcat_pclose(ctx, fp);
EXPECT_LT(4, count);
}
@@ -115,7 +127,7 @@
static const size_t retry = 4;
size_t errors = retry;
size_t num = 0;
- for(;;) {
+ for (;;) {
log_time ts(CLOCK_MONOTONIC);
if (__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) >= 0) {
if (++num >= (size_t)count) {
@@ -124,7 +136,7 @@
return num;
}
errors = retry;
- usleep(100); // ~32 per timer tick, we are a spammer regardless
+ usleep(100); // ~32 per timer tick, we are a spammer regardless
} else if (--errors <= 0) {
return num;
}
@@ -134,22 +146,22 @@
}
TEST(logcat, year) {
-
if (android_log_clockid() == CLOCK_MONOTONIC) {
fprintf(stderr, "Skipping test, logd is monotonic time\n");
return;
}
int count;
- int tries = 3; // in case run too soon after system start or buffer clear
+ int tries = 3; // in case run too soon after system start or buffer clear
do {
- FILE *fp;
+ FILE* fp;
+ logcat_define(ctx);
char needle[32];
time_t now;
time(&now);
- struct tm *ptm;
+ struct tm* ptm;
#if !defined(_WIN32)
struct tm tmBuf;
ptm = localtime_r(&now, &tmBuf);
@@ -158,9 +170,10 @@
#endif
strftime(needle, sizeof(needle), "[ %Y-", ptm);
- ASSERT_TRUE(NULL != (fp = popen(
- "logcat -v long -v year -b all -t 3 2>/dev/null",
- "r")));
+ ASSERT_TRUE(
+ NULL !=
+ (fp = logcat_popen(
+ ctx, "logcat -v long -v year -b all -t 3 2>/dev/null")));
char buffer[BIG_BUFFER];
@@ -171,7 +184,7 @@
++count;
}
}
- pclose(fp);
+ logcat_pclose(ctx, fp);
} while ((count < 3) && --tries && inject(3 - count));
@@ -179,21 +192,21 @@
}
// Return a pointer to each null terminated -v long time field.
-char *fgetLongTime(char *buffer, size_t buflen, FILE *fp) {
+static char* fgetLongTime(char* buffer, size_t buflen, FILE* fp) {
while (fgets(buffer, buflen, fp)) {
- char *cp = buffer;
+ char* cp = buffer;
if (*cp != '[') {
continue;
}
while (*++cp == ' ') {
;
}
- char *ep = cp;
+ char* ep = cp;
while (isdigit(*ep)) {
++ep;
}
if ((*ep != '-') && (*ep != '.')) {
- continue;
+ continue;
}
// Find PID field
while (((ep = strchr(ep, ':'))) && (*++ep != ' ')) {
@@ -211,21 +224,22 @@
}
TEST(logcat, tz) {
-
if (android_log_clockid() == CLOCK_MONOTONIC) {
fprintf(stderr, "Skipping test, logd is monotonic time\n");
return;
}
- int tries = 4; // in case run too soon after system start or buffer clear
+ int tries = 4; // in case run too soon after system start or buffer clear
int count;
do {
- FILE *fp;
+ FILE* fp;
+ logcat_define(ctx);
- ASSERT_TRUE(NULL != (fp = popen(
- "logcat -v long -v America/Los_Angeles -b all -t 3 2>/dev/null",
- "r")));
+ ASSERT_TRUE(NULL !=
+ (fp = logcat_popen(ctx,
+ "logcat -v long -v America/Los_Angeles "
+ "-b all -t 3 2>/dev/null")));
char buffer[BIG_BUFFER];
@@ -239,7 +253,7 @@
}
}
- pclose(fp);
+ logcat_pclose(ctx, fp);
} while ((count < 3) && --tries && inject(3 - count));
@@ -247,11 +261,13 @@
}
TEST(logcat, ntz) {
- FILE *fp;
+ FILE* fp;
+ logcat_define(ctx);
- ASSERT_TRUE(NULL != (fp = popen(
- "logcat -v long -v America/Los_Angeles -v zone -b all -t 3 2>/dev/null",
- "r")));
+ ASSERT_TRUE(NULL !=
+ (fp = logcat_popen(ctx,
+ "logcat -v long -v America/Los_Angeles -v "
+ "zone -b all -t 3 2>/dev/null")));
char buffer[BIG_BUFFER];
@@ -263,13 +279,13 @@
}
}
- pclose(fp);
+ logcat_pclose(ctx, fp);
ASSERT_EQ(0, count);
}
-void do_tail(int num) {
- int tries = 4; // in case run too soon after system start or buffer clear
+static void do_tail(int num) {
+ int tries = 4; // in case run too soon after system start or buffer clear
int count;
if (num > 10) ++tries;
@@ -278,10 +294,11 @@
char buffer[BIG_BUFFER];
snprintf(buffer, sizeof(buffer),
- "logcat -v long -b all -t %d 2>/dev/null", num);
+ "ANDROID_PRINTF_LOG=long logcat -b all -t %d 2>/dev/null", num);
- FILE *fp;
- ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+ FILE* fp;
+ logcat_define(ctx);
+ ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
count = 0;
@@ -289,7 +306,7 @@
++count;
}
- pclose(fp);
+ logcat_pclose(ctx, fp);
} while ((count < num) && --tries && inject(num - count));
@@ -313,25 +330,27 @@
}
TEST(logcat, tail_time) {
- FILE *fp;
+ FILE* fp;
int count;
char buffer[BIG_BUFFER];
- char *last_timestamp = NULL;
+ char* last_timestamp = NULL;
// Hard to predict 100% if first (overlap) or second line will match.
// -v nsec will in a substantial majority be the second line.
- char *first_timestamp = NULL;
- char *second_timestamp = NULL;
- char *input;
+ char* first_timestamp = NULL;
+ char* second_timestamp = NULL;
+ char* input;
- int tries = 4; // in case run too soon after system start or buffer clear
+ int tries = 4; // in case run too soon after system start or buffer clear
do {
- ASSERT_TRUE(NULL != (fp = popen("logcat"
- " -v long"
- " -v nsec"
- " -b all"
- " -t 10"
- " 2>&1", "r")));
+ logcat_define(ctx);
+ ASSERT_TRUE(NULL != (fp = logcat_popen(ctx,
+ "logcat"
+ " -v long"
+ " -v nsec"
+ " -b all"
+ " -t 10"
+ " 2>&1")));
count = 0;
while ((input = fgetLongTime(buffer, sizeof(buffer), fp))) {
@@ -344,28 +363,30 @@
free(last_timestamp);
last_timestamp = strdup(input);
}
- pclose(fp);
+ logcat_pclose(ctx, fp);
} while ((count < 10) && --tries && inject(10 - count));
- EXPECT_EQ(10, count); // We want _some_ history, too small, falses below
+ EXPECT_EQ(10, count); // We want _some_ history, too small, falses below
EXPECT_TRUE(last_timestamp != NULL);
EXPECT_TRUE(first_timestamp != NULL);
EXPECT_TRUE(second_timestamp != NULL);
- snprintf(buffer, sizeof(buffer), "logcat"
- " -v long"
- " -v nsec"
- " -b all"
- " -t '%s'"
- " 2>&1",
- first_timestamp);
- ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+ snprintf(buffer, sizeof(buffer),
+ "logcat"
+ " -v long"
+ " -v nsec"
+ " -b all"
+ " -t '%s'"
+ " 2>&1",
+ first_timestamp);
+ logcat_define(ctx);
+ ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
int second_count = 0;
int last_timestamp_count = -1;
- --count; // One less unless we match the first_timestamp
+ --count; // One less unless we match the first_timestamp
bool found = false;
while ((input = fgetLongTime(buffer, sizeof(buffer), fp))) {
++second_count;
@@ -381,9 +402,8 @@
found = !strcmp(input, first_timestamp);
if (found) {
++count;
- GTEST_LOG_(INFO) << "input = first("
- << first_timestamp
- << ")\n";
+ GTEST_LOG_(INFO)
+ << "input = first(" << first_timestamp << ")\n";
}
free(first_timestamp);
first_timestamp = NULL;
@@ -391,11 +411,8 @@
if (second_timestamp) {
found = found || !strcmp(input, second_timestamp);
if (!found) {
- GTEST_LOG_(INFO) << "input("
- << input
- << ") != second("
- << second_timestamp
- << ")\n";
+ GTEST_LOG_(INFO) << "input(" << input << ") != second("
+ << second_timestamp << ")\n";
}
free(second_timestamp);
second_timestamp = NULL;
@@ -404,7 +421,7 @@
last_timestamp_count = second_count;
}
}
- pclose(fp);
+ logcat_pclose(ctx, fp);
EXPECT_TRUE(found);
if (!found) {
@@ -436,10 +453,11 @@
ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
- FILE *fp;
- ASSERT_TRUE(NULL != (fp = popen(
- "logcat -v brief -b events -t 100 2>/dev/null",
- "r")));
+ FILE* fp;
+ logcat_define(ctx);
+ ASSERT_TRUE(NULL !=
+ (fp = logcat_popen(
+ ctx, "logcat -v brief -b events -t 100 2>/dev/null")));
char buffer[BIG_BUFFER];
@@ -449,27 +467,28 @@
int p;
unsigned long long t;
- if ((2 != sscanf(buffer, "I/[0] ( %d): %llu", &p, &t))
- || (p != pid)) {
+ if ((2 != sscanf(buffer, "I/[0] ( %d): %llu", &p, &t)) ||
+ (p != pid)) {
continue;
}
- log_time tx((const char *) &t);
+ log_time tx((const char*)&t);
if (ts == tx) {
++count;
}
}
- pclose(fp);
+ logcat_pclose(ctx, fp);
ASSERT_EQ(1, count);
}
-int get_groups(const char *cmd) {
- FILE *fp;
+static int get_groups(const char* cmd) {
+ FILE* fp;
+ logcat_define(ctx);
// NB: crash log only available in user space
- EXPECT_TRUE(NULL != (fp = popen(cmd, "r")));
+ EXPECT_TRUE(NULL != (fp = logcat_popen(ctx, cmd)));
if (fp == NULL) {
return 0;
@@ -487,75 +506,78 @@
size = consumed = max = payload = 0;
// NB: crash log can be very small, not hit a Kb of consumed space
// doubly lucky we are not including it.
- if (6 != sscanf(buffer, "%*s ring buffer is %d%2s (%d%2s consumed),"
- " max entry is %db, max payload is %db",
- &size, size_mult, &consumed, consumed_mult,
- &max, &payload)) {
+ if (6 != sscanf(buffer,
+ "%*s ring buffer is %d%2s (%d%2s consumed),"
+ " max entry is %db, max payload is %db",
+ &size, size_mult, &consumed, consumed_mult, &max,
+ &payload)) {
fprintf(stderr, "WARNING: Parse error: %s", buffer);
continue;
}
full_size = size;
- switch(size_mult[0]) {
- case 'G':
- full_size *= 1024;
+ switch (size_mult[0]) {
+ case 'G':
+ full_size *= 1024;
/* FALLTHRU */
- case 'M':
- full_size *= 1024;
+ case 'M':
+ full_size *= 1024;
/* FALLTHRU */
- case 'K':
- full_size *= 1024;
+ case 'K':
+ full_size *= 1024;
/* FALLTHRU */
- case 'b':
- break;
+ case 'b':
+ break;
}
full_consumed = consumed;
- switch(consumed_mult[0]) {
- case 'G':
- full_consumed *= 1024;
+ switch (consumed_mult[0]) {
+ case 'G':
+ full_consumed *= 1024;
/* FALLTHRU */
- case 'M':
- full_consumed *= 1024;
+ case 'M':
+ full_consumed *= 1024;
/* FALLTHRU */
- case 'K':
- full_consumed *= 1024;
+ case 'K':
+ full_consumed *= 1024;
/* FALLTHRU */
- case 'b':
- break;
+ case 'b':
+ break;
}
EXPECT_GT((full_size * 9) / 4, full_consumed);
EXPECT_GT(full_size, max);
EXPECT_GT(max, payload);
- if ((((full_size * 9) / 4) >= full_consumed)
- && (full_size > max)
- && (max > payload)) {
+ if ((((full_size * 9) / 4) >= full_consumed) && (full_size > max) &&
+ (max > payload)) {
++count;
}
}
- pclose(fp);
+ logcat_pclose(ctx, fp);
return count;
}
TEST(logcat, get_size) {
- ASSERT_EQ(4, get_groups(
- "logcat -v brief -b radio -b events -b system -b main -g 2>/dev/null"));
+ ASSERT_EQ(4, get_groups("logcat -v brief -b radio -b events -b system -b "
+ "main -g 2>/dev/null"));
}
// duplicate test for get_size, but use comma-separated list of buffers
TEST(logcat, multiple_buffer) {
- ASSERT_EQ(4, get_groups(
- "logcat -v brief -b radio,events,system,main -g 2>/dev/null"));
+ ASSERT_EQ(
+ 4, get_groups(
+ "logcat -v brief -b radio,events,system,main -g 2>/dev/null"));
}
TEST(logcat, bad_buffer) {
- ASSERT_EQ(0, get_groups(
- "logcat -v brief -b radio,events,bogo,system,main -g 2>/dev/null"));
+ ASSERT_EQ(
+ 0,
+ get_groups(
+ "logcat -v brief -b radio,events,bogo,system,main -g 2>/dev/null"));
}
-static void caught_blocking(int signum)
-{
+#ifndef logcat
+static void caught_blocking(int signum) {
unsigned long long v = 0xDEADBEEFA55A0000ULL;
v += getpid() & 0xFFFF;
@@ -565,7 +587,7 @@
}
TEST(logcat, blocking) {
- FILE *fp;
+ FILE* fp;
unsigned long long v = 0xDEADBEEFA55F0000ULL;
pid_t pid = getpid();
@@ -576,10 +598,11 @@
v &= 0xFFFFFFFFFFFAFFFFULL;
- ASSERT_TRUE(NULL != (fp = popen(
- "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
- " logcat -v brief -b events 2>&1",
- "r")));
+ ASSERT_TRUE(
+ NULL !=
+ (fp = popen("( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
+ " logcat -v brief -b events 2>&1",
+ "r")));
char buffer[BIG_BUFFER];
@@ -590,7 +613,6 @@
signal(SIGALRM, caught_blocking);
alarm(2);
while (fgets(buffer, sizeof(buffer), fp)) {
-
if (!strncmp(buffer, "DONE", 4)) {
break;
}
@@ -600,8 +622,7 @@
int p;
unsigned long long l;
- if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l))
- || (p != pid)) {
+ if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) || (p != pid)) {
continue;
}
@@ -624,8 +645,7 @@
EXPECT_EQ(1, signals);
}
-static void caught_blocking_tail(int signum)
-{
+static void caught_blocking_tail(int signum) {
unsigned long long v = 0xA55ADEADBEEF0000ULL;
v += getpid() & 0xFFFF;
@@ -635,7 +655,7 @@
}
TEST(logcat, blocking_tail) {
- FILE *fp;
+ FILE* fp;
unsigned long long v = 0xA55FDEADBEEF0000ULL;
pid_t pid = getpid();
@@ -646,10 +666,11 @@
v &= 0xFFFAFFFFFFFFFFFFULL;
- ASSERT_TRUE(NULL != (fp = popen(
- "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
- " logcat -v brief -b events -T 5 2>&1",
- "r")));
+ ASSERT_TRUE(
+ NULL !=
+ (fp = popen("( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
+ " logcat -v brief -b events -T 5 2>&1",
+ "r")));
char buffer[BIG_BUFFER];
@@ -660,7 +681,6 @@
signal(SIGALRM, caught_blocking_tail);
alarm(2);
while (fgets(buffer, sizeof(buffer), fp)) {
-
if (!strncmp(buffer, "DONE", 4)) {
break;
}
@@ -670,8 +690,7 @@
int p;
unsigned long long l;
- if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l))
- || (p != pid)) {
+ if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) || (p != pid)) {
continue;
}
@@ -695,13 +714,13 @@
EXPECT_EQ(1, signals);
}
+#endif
// meant to be handed to ASSERT_FALSE / EXPECT_FALSE to expand the message
static testing::AssertionResult IsFalse(int ret, const char* command) {
- return ret ?
- (testing::AssertionSuccess() <<
- "ret=" << ret << " command=\"" << command << "\"") :
- testing::AssertionFailure();
+ return ret ? (testing::AssertionSuccess()
+ << "ret=" << ret << " command=\"" << command << "\"")
+ : testing::AssertionFailure();
}
TEST(logcat, logrotate) {
@@ -709,17 +728,18 @@
char buf[sizeof(form)];
ASSERT_TRUE(NULL != mkdtemp(strcpy(buf, form)));
- static const char comm[] = "logcat -b radio -b events -b system -b main"
- " -d -f %s/log.txt -n 7 -r 1";
+ static const char comm[] =
+ "logcat -b radio -b events -b system -b main"
+ " -d -f %s/log.txt -n 7 -r 1";
char command[sizeof(buf) + sizeof(comm)];
snprintf(command, sizeof(command), comm, buf);
int ret;
- EXPECT_FALSE(IsFalse(ret = system(command), command));
+ EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
if (!ret) {
snprintf(command, sizeof(command), "ls -s %s 2>/dev/null", buf);
- FILE *fp;
+ FILE* fp;
EXPECT_TRUE(NULL != (fp = popen(command, "r")));
if (fp) {
char buffer[BIG_BUFFER];
@@ -731,7 +751,7 @@
char c;
if ((2 == sscanf(buffer, "%d log.tx%c", &num, &c)) &&
- (num <= 40)) {
+ (num <= 40)) {
++count;
} else if (strncmp(buffer, total, sizeof(total) - 1)) {
fprintf(stderr, "WARNING: Parse error: %s", buffer);
@@ -749,21 +769,23 @@
}
TEST(logcat, logrotate_suffix) {
- static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+ static const char tmp_out_dir_form[] =
+ "/data/local/tmp/logcat.logrotate.XXXXXX";
char tmp_out_dir[sizeof(tmp_out_dir_form)];
ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
- static const char logcat_cmd[] = "logcat -b radio -b events -b system -b main"
- " -d -f %s/log.txt -n 10 -r 1";
+ static const char logcat_cmd[] =
+ "logcat -b radio -b events -b system -b main"
+ " -d -f %s/log.txt -n 10 -r 1";
char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd)];
snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir);
int ret;
- EXPECT_FALSE(IsFalse(ret = system(command), command));
+ EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
if (!ret) {
snprintf(command, sizeof(command), "ls %s 2>/dev/null", tmp_out_dir);
- FILE *fp;
+ FILE* fp;
EXPECT_TRUE(NULL != (fp = popen(command, "r")));
char buffer[BIG_BUFFER];
int log_file_count = 0;
@@ -774,21 +796,24 @@
strlen(rotated_log_filename_prefix);
static const char log_filename[] = "log.txt";
- if (!strncmp(buffer, rotated_log_filename_prefix, rotated_log_filename_prefix_len)) {
- // Rotated file should have form log.txt.##
- char* rotated_log_filename_suffix = buffer + rotated_log_filename_prefix_len;
- char* endptr;
- const long int suffix_value = strtol(rotated_log_filename_suffix, &endptr, 10);
- EXPECT_EQ(rotated_log_filename_suffix + 2, endptr);
- EXPECT_LE(suffix_value, 10);
- EXPECT_GT(suffix_value, 0);
- ++log_file_count;
- continue;
+ if (!strncmp(buffer, rotated_log_filename_prefix,
+ rotated_log_filename_prefix_len)) {
+ // Rotated file should have form log.txt.##
+ char* rotated_log_filename_suffix =
+ buffer + rotated_log_filename_prefix_len;
+ char* endptr;
+ const long int suffix_value =
+ strtol(rotated_log_filename_suffix, &endptr, 10);
+ EXPECT_EQ(rotated_log_filename_suffix + 2, endptr);
+ EXPECT_LE(suffix_value, 10);
+ EXPECT_GT(suffix_value, 0);
+ ++log_file_count;
+ continue;
}
if (!strncmp(buffer, log_filename, strlen(log_filename))) {
- ++log_file_count;
- continue;
+ ++log_file_count;
+ continue;
}
fprintf(stderr, "ERROR: Unexpected file: %s", buffer);
@@ -802,24 +827,26 @@
}
TEST(logcat, logrotate_continue) {
- static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+ static const char tmp_out_dir_form[] =
+ "/data/local/tmp/logcat.logrotate.XXXXXX";
char tmp_out_dir[sizeof(tmp_out_dir_form)];
ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
static const char log_filename[] = "log.txt";
- static const char logcat_cmd[] = "logcat -b all -v nsec -d -f %s/%s -n 256 -r 1024";
+ static const char logcat_cmd[] =
+ "logcat -b all -v nsec -d -f %s/%s -n 256 -r 1024";
static const char cleanup_cmd[] = "rm -rf %s";
char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename)];
snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
int ret;
- EXPECT_FALSE(IsFalse(ret = system(command), command));
+ EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
if (ret) {
snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(IsFalse(system(command), command));
return;
}
- FILE *fp;
+ FILE* fp;
snprintf(command, sizeof(command), "%s/%s", tmp_out_dir, log_filename);
EXPECT_TRUE(NULL != ((fp = fopen(command, "r"))));
if (!fp) {
@@ -827,10 +854,11 @@
EXPECT_FALSE(IsFalse(system(command), command));
return;
}
- char *line = NULL;
- char *last_line = NULL; // this line is allowed to stutter, one-line overlap
- char *second_last_line = NULL; // should never stutter
- char *first_line = NULL; // help diagnose failure?
+ char* line = NULL;
+ char* last_line =
+ NULL; // this line is allowed to stutter, one-line overlap
+ char* second_last_line = NULL; // should never stutter
+ char* first_line = NULL; // help diagnose failure?
size_t len = 0;
while (getline(&line, &len, fp) != -1) {
if (!first_line) {
@@ -861,7 +889,7 @@
// re-run the command, it should only add a few lines more content if it
// continues where it left off.
snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
- EXPECT_FALSE(IsFalse(ret = system(command), command));
+ EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
if (ret) {
snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(IsFalse(system(command), command));
@@ -869,7 +897,8 @@
free(first_line);
return;
}
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir),
+ closedir);
EXPECT_NE(nullptr, dir);
if (!dir) {
snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
@@ -878,7 +907,7 @@
free(first_line);
return;
}
- struct dirent *entry;
+ struct dirent* entry;
unsigned count = 0;
while ((entry = readdir(dir.get()))) {
if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
@@ -904,15 +933,15 @@
unlink(command);
}
if (count > 1) {
- char *brk = strpbrk(second_last_line, "\r\n");
+ char* brk = strpbrk(second_last_line, "\r\n");
if (!brk) brk = second_last_line + strlen(second_last_line);
fprintf(stderr, "\"%.*s\" occurred %u times\n",
- (int)(brk - second_last_line), second_last_line, count);
+ (int)(brk - second_last_line), second_last_line, count);
if (first_line) {
brk = strpbrk(first_line, "\r\n");
if (!brk) brk = first_line + strlen(first_line);
fprintf(stderr, "\"%.*s\" was first line, fault?\n",
- (int)(brk - first_line), first_line);
+ (int)(brk - first_line), first_line);
}
}
free(second_last_line);
@@ -923,7 +952,8 @@
}
TEST(logcat, logrotate_clear) {
- static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+ static const char tmp_out_dir_form[] =
+ "/data/local/tmp/logcat.logrotate.XXXXXX";
char tmp_out_dir[sizeof(tmp_out_dir_form)];
ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
@@ -932,28 +962,30 @@
static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n %d -r 1";
static const char clear_cmd[] = " -c";
static const char cleanup_cmd[] = "rm -rf %s";
- char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename) + sizeof(clear_cmd) + 32];
+ char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) +
+ sizeof(log_filename) + sizeof(clear_cmd) + 32];
// Run command with all data
{
- snprintf(command, sizeof(command) - sizeof(clear_cmd),
- logcat_cmd, tmp_out_dir, log_filename, num_val);
+ snprintf(command, sizeof(command) - sizeof(clear_cmd), logcat_cmd,
+ tmp_out_dir, log_filename, num_val);
int ret;
- EXPECT_FALSE(IsFalse(ret = system(command), command));
+ EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
if (ret) {
snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(IsFalse(system(command), command));
return;
}
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir),
+ closedir);
EXPECT_NE(nullptr, dir);
if (!dir) {
snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(IsFalse(system(command), command));
return;
}
- struct dirent *entry;
+ struct dirent* entry;
unsigned count = 0;
while ((entry = readdir(dir.get()))) {
if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
@@ -969,21 +1001,22 @@
strcat(command, clear_cmd);
int ret;
- EXPECT_FALSE(IsFalse(ret = system(command), command));
+ EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
if (ret) {
snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(system(command));
EXPECT_FALSE(IsFalse(system(command), command));
return;
}
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir),
+ closedir);
EXPECT_NE(nullptr, dir);
if (!dir) {
snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(IsFalse(system(command), command));
return;
}
- struct dirent *entry;
+ struct dirent* entry;
unsigned count = 0;
while ((entry = readdir(dir.get()))) {
if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
@@ -999,24 +1032,25 @@
EXPECT_FALSE(IsFalse(system(command), command));
}
-static int logrotate_count_id(const char *logcat_cmd, const char *tmp_out_dir) {
-
+static int logrotate_count_id(const char* logcat_cmd, const char* tmp_out_dir) {
static const char log_filename[] = "log.txt";
- char command[strlen(tmp_out_dir) + strlen(logcat_cmd) + strlen(log_filename) + 32];
+ char command[strlen(tmp_out_dir) + strlen(logcat_cmd) +
+ strlen(log_filename) + 32];
snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
- int ret;
- EXPECT_FALSE(IsFalse(ret = system(command), command));
+ int ret = logcat_system(command);
if (ret) {
+ fprintf(stderr, "system(\"%s\")=%d", command, ret);
return -1;
}
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
- EXPECT_NE(nullptr, dir);
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir),
+ closedir);
if (!dir) {
+ fprintf(stderr, "opendir(\"%s\") failed", tmp_out_dir);
return -1;
}
- struct dirent *entry;
+ struct dirent* entry;
int count = 0;
while ((entry = readdir(dir.get()))) {
if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
@@ -1028,9 +1062,12 @@
}
TEST(logcat, logrotate_id) {
- static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n 32 -r 1 --id=test";
- static const char logcat_short_cmd[] = "logcat -b all -t 10 -f %s/%s -n 32 -r 1 --id=test";
- static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+ static const char logcat_cmd[] =
+ "logcat -b all -d -f %s/%s -n 32 -r 1 --id=test";
+ static const char logcat_short_cmd[] =
+ "logcat -b all -t 10 -f %s/%s -n 32 -r 1 --id=test";
+ static const char tmp_out_dir_form[] =
+ "/data/local/tmp/logcat.logrotate.XXXXXX";
static const char log_filename[] = "log.txt";
char tmp_out_dir[strlen(tmp_out_dir_form) + 1];
ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
@@ -1047,19 +1084,21 @@
unlink(id_file);
EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
- FILE *fp = fopen(id_file, "w");
+ FILE* fp = fopen(id_file, "w");
if (fp) {
fprintf(fp, "not_a_test");
fclose(fp);
}
if (getuid() != 0) {
- chmod(id_file, 0); // API to preserve content even with signature change
+ chmod(id_file,
+ 0); // API to preserve content even with signature change
ASSERT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
chmod(id_file, 0600);
}
int new_signature;
- EXPECT_LE(2, (new_signature = logrotate_count_id(logcat_short_cmd, tmp_out_dir)));
+ EXPECT_LE(
+ 2, (new_signature = logrotate_count_id(logcat_short_cmd, tmp_out_dir)));
EXPECT_GT(34, new_signature);
static const char cleanup_cmd[] = "rm -rf %s";
@@ -1069,13 +1108,15 @@
}
TEST(logcat, logrotate_nodir) {
- // expect logcat to error out on writing content and exit(1) for nodir
- EXPECT_EQ(W_EXITCODE(1, 0),
- system("logcat -b all -d"
- " -f /das/nein/gerfingerpoken/logcat/log.txt"
- " -n 256 -r 1024"));
+ // expect logcat to error out on writing content and not exit(0) for nodir
+ static const char command[] =
+ "logcat -b all -d"
+ " -f /das/nein/gerfingerpoken/logcat/log.txt"
+ " -n 256 -r 1024";
+ EXPECT_FALSE(IsFalse(0 == logcat_system(command), command));
}
+#ifndef logcat
static void caught_blocking_clear(int signum) {
unsigned long long v = 0xDEADBEEFA55C0000ULL;
@@ -1086,7 +1127,7 @@
}
TEST(logcat, blocking_clear) {
- FILE *fp;
+ FILE* fp;
unsigned long long v = 0xDEADBEEFA55C0000ULL;
pid_t pid = getpid();
@@ -1095,12 +1136,13 @@
// This test is racey; an event occurs between clear and dump.
// We accept that we will get a false positive, but never a false negative.
- ASSERT_TRUE(NULL != (fp = popen(
- "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
- " logcat -b events -c 2>&1 ;"
- " logcat -b events -g 2>&1 ;"
- " logcat -v brief -b events 2>&1",
- "r")));
+ ASSERT_TRUE(
+ NULL !=
+ (fp = popen("( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
+ " logcat -b events -c 2>&1 ;"
+ " logcat -b events -g 2>&1 ;"
+ " logcat -v brief -b events 2>&1",
+ "r")));
char buffer[BIG_BUFFER];
@@ -1112,7 +1154,6 @@
signal(SIGALRM, caught_blocking_clear);
alarm(2);
while (fgets(buffer, sizeof(buffer), fp)) {
-
if (!strncmp(buffer, "clearLog: ", 10)) {
fprintf(stderr, "WARNING: Test lacks permission to run :-(\n");
count = signals = 1;
@@ -1126,37 +1167,38 @@
int size, consumed, max, payload;
char size_mult[3], consumed_mult[3];
size = consumed = max = payload = 0;
- if (6 == sscanf(buffer, "events: ring buffer is %d%2s (%d%2s consumed),"
- " max entry is %db, max payload is %db",
- &size, size_mult, &consumed, consumed_mult,
- &max, &payload)) {
+ if (6 == sscanf(buffer,
+ "events: ring buffer is %d%2s (%d%2s consumed),"
+ " max entry is %db, max payload is %db",
+ &size, size_mult, &consumed, consumed_mult, &max,
+ &payload)) {
long full_size = size, full_consumed = consumed;
- switch(size_mult[0]) {
- case 'G':
- full_size *= 1024;
+ switch (size_mult[0]) {
+ case 'G':
+ full_size *= 1024;
/* FALLTHRU */
- case 'M':
- full_size *= 1024;
+ case 'M':
+ full_size *= 1024;
/* FALLTHRU */
- case 'K':
- full_size *= 1024;
+ case 'K':
+ full_size *= 1024;
/* FALLTHRU */
- case 'b':
- break;
+ case 'b':
+ break;
}
- switch(consumed_mult[0]) {
- case 'G':
- full_consumed *= 1024;
+ switch (consumed_mult[0]) {
+ case 'G':
+ full_consumed *= 1024;
/* FALLTHRU */
- case 'M':
- full_consumed *= 1024;
+ case 'M':
+ full_consumed *= 1024;
/* FALLTHRU */
- case 'K':
- full_consumed *= 1024;
+ case 'K':
+ full_consumed *= 1024;
/* FALLTHRU */
- case 'b':
- break;
+ case 'b':
+ break;
}
EXPECT_GT(full_size, full_consumed);
EXPECT_GT(full_size, max);
@@ -1172,8 +1214,7 @@
int p;
unsigned long long l;
- if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l))
- || (p != pid)) {
+ if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) || (p != pid)) {
continue;
}
@@ -1199,11 +1240,13 @@
EXPECT_EQ(1, signals);
}
+#endif
-static bool get_white_black(char **list) {
- FILE *fp;
+static bool get_white_black(char** list) {
+ FILE* fp;
+ logcat_define(ctx);
- fp = popen("logcat -p 2>/dev/null", "r");
+ fp = logcat_popen(ctx, "logcat -p 2>/dev/null");
if (fp == NULL) {
fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n");
return false;
@@ -1212,12 +1255,12 @@
char buffer[BIG_BUFFER];
while (fgets(buffer, sizeof(buffer), fp)) {
- char *hold = *list;
- char *buf = buffer;
+ char* hold = *list;
+ char* buf = buffer;
while (isspace(*buf)) {
++buf;
}
- char *end = buf + strlen(buf);
+ char* end = buf + strlen(buf);
while (isspace(*--end) && (end >= buf)) {
*end = '\0';
}
@@ -1231,28 +1274,29 @@
asprintf(list, "%s", buf);
}
}
- pclose(fp);
+ logcat_pclose(ctx, fp);
return *list != NULL;
}
-static bool set_white_black(const char *list) {
- FILE *fp;
+static bool set_white_black(const char* list) {
+ FILE* fp;
+ logcat_define(ctx);
char buffer[BIG_BUFFER];
snprintf(buffer, sizeof(buffer), "logcat -P '%s' 2>&1", list ? list : "");
- fp = popen(buffer, "r");
+ fp = logcat_popen(ctx, buffer);
if (fp == NULL) {
fprintf(stderr, "ERROR: %s\n", buffer);
return false;
}
while (fgets(buffer, sizeof(buffer), fp)) {
- char *buf = buffer;
+ char* buf = buffer;
while (isspace(*buf)) {
++buf;
}
- char *end = buf + strlen(buf);
+ char* end = buf + strlen(buf);
while ((end > buf) && isspace(*--end)) {
*end = '\0';
}
@@ -1260,15 +1304,15 @@
continue;
}
fprintf(stderr, "%s\n", buf);
- pclose(fp);
+ logcat_pclose(ctx, fp);
return false;
}
- return pclose(fp) == 0;
+ return logcat_pclose(ctx, fp) == 0;
}
TEST(logcat, white_black_adjust) {
- char *list = NULL;
- char *adjust = NULL;
+ char* list = NULL;
+ char* adjust = NULL;
get_white_black(&list);
@@ -1297,55 +1341,72 @@
}
TEST(logcat, regex) {
- FILE *fp;
+ FILE* fp;
+ logcat_define(ctx);
int count = 0;
char buffer[BIG_BUFFER];
+// Have to make liblogcat data unique from logcat data injection
+#ifdef logcat
+#define logcat_regex_prefix "lolcat_test"
+#else
+#define logcat_regex_prefix "logcat_test"
+#endif
- snprintf(buffer, sizeof(buffer), "logcat --pid %d -d -e logcat_test_a+b", getpid());
+ snprintf(buffer, sizeof(buffer),
+ "logcat --pid %d -d -e " logcat_regex_prefix "_a+b", getpid());
- LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_ab"));
- LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_b"));
- LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_aaaab"));
- LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_aaaa"));
-
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
+ logcat_regex_prefix "_ab"));
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
+ logcat_regex_prefix "_b"));
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
+ logcat_regex_prefix "_aaaab"));
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
+ logcat_regex_prefix "_aaaa"));
// Let the logs settle
sleep(1);
- ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+ ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
while (fgets(buffer, sizeof(buffer), fp)) {
if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
continue;
}
- EXPECT_TRUE(strstr(buffer, "logcat_test_") != NULL);
+ EXPECT_TRUE(strstr(buffer, logcat_regex_prefix "_") != NULL);
count++;
}
- pclose(fp);
+ logcat_pclose(ctx, fp);
ASSERT_EQ(2, count);
}
TEST(logcat, maxcount) {
- FILE *fp;
+ FILE* fp;
+ logcat_define(ctx);
int count = 0;
char buffer[BIG_BUFFER];
- snprintf(buffer, sizeof(buffer), "logcat --pid %d -d --max-count 3", getpid());
+ snprintf(buffer, sizeof(buffer), "logcat --pid %d -d --max-count 3",
+ getpid());
- LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
- LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
- LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
- LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+ LOG_FAILURE_RETRY(
+ __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+ LOG_FAILURE_RETRY(
+ __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+ LOG_FAILURE_RETRY(
+ __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+ LOG_FAILURE_RETRY(
+ __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
// Let the logs settle
sleep(1);
- ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+ ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
while (fgets(buffer, sizeof(buffer), fp)) {
if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
@@ -1355,7 +1416,7 @@
count++;
}
- pclose(fp);
+ logcat_pclose(ctx, fp);
ASSERT_EQ(3, count);
}
@@ -1367,8 +1428,18 @@
;
static bool End_to_End(const char* tag, const char* fmt, ...) {
- FILE *fp = popen("logcat -v brief -b events -v descriptive -t 100 2>/dev/null", "r");
- if (!fp) return false;
+ logcat_define(ctx);
+ FILE* fp = logcat_popen(ctx,
+ "logcat"
+ " -v brief"
+ " -b events"
+ " -v descriptive"
+ " -t 100"
+ " 2>/dev/null");
+ if (!fp) {
+ fprintf(stderr, "End_to_End: popen failed");
+ return false;
+ }
char buffer[BIG_BUFFER];
va_list ap;
@@ -1377,7 +1448,7 @@
vsnprintf(buffer, sizeof(buffer), fmt, ap);
va_end(ap);
- char *str = NULL;
+ char* str = NULL;
asprintf(&str, "I/%s ( %%d):%%c%s%%c", tag, buffer);
std::string expect(str);
free(str);
@@ -1399,15 +1470,19 @@
}
}
- pclose(fp);
+ logcat_pclose(ctx, fp);
if ((count == 0) && (lastMatch.length() > 0)) {
// Help us pinpoint where things went wrong ...
fprintf(stderr, "Closest match for\n %s\n is\n %s",
expect.c_str(), lastMatch.c_str());
+ } else if (count > 2) {
+ fprintf(stderr, "Too many matches (%d) for %s\n", count, expect.c_str());
}
- return count == 1;
+ // Expect one the first time around as either liblogcat.descriptive or
+ // logcat.descriptive. Expect two the second time as the other.
+ return count == 1 || count == 2;
}
TEST(logcat, descriptive) {
@@ -1422,8 +1497,8 @@
static const char theAnswer[] = "what is five by seven";
ctx << theAnswer;
ctx.write();
- EXPECT_TRUE(End_to_End(hhgtg.tagStr,
- "to life the universe etc=%s", theAnswer));
+ EXPECT_TRUE(
+ End_to_End(hhgtg.tagStr, "to life the universe etc=%s", theAnswer));
}
{
@@ -1434,8 +1509,7 @@
ctx << id << (int32_t)42 << (int32_t)-1 << (int32_t)0;
ctx.write();
EXPECT_TRUE(End_to_End(sync.tagStr,
- "[id=%s,event=42,source=-1,account=0]",
- id));
+ "[id=%s,event=42,source=-1,account=0]", id));
}
// Partial match to description
@@ -1443,9 +1517,7 @@
android_log_event_list ctx(sync.tagNo);
ctx << id << (int32_t)43 << (int64_t)-1 << (int32_t)0;
ctx.write();
- EXPECT_TRUE(End_to_End(sync.tagStr,
- "[id=%s,event=43,-1,0]",
- id));
+ EXPECT_TRUE(End_to_End(sync.tagStr, "[id=%s,event=43,-1,0]", id));
}
// Negative Test of End_to_End, ensure it is working
@@ -1454,9 +1526,8 @@
ctx << id << (int32_t)44 << (int32_t)-1 << (int64_t)0;
ctx.write();
fprintf(stderr, "Expect a \"Closest match\" message\n");
- EXPECT_FALSE(End_to_End(sync.tagStr,
- "[id=%s,event=44,source=-1,account=0]",
- id));
+ EXPECT_FALSE(End_to_End(
+ sync.tagStr, "[id=%s,event=44,source=-1,account=0]", id));
}
}
@@ -1466,16 +1537,16 @@
android_log_event_list ctx(sync.tagNo);
ctx << (uint64_t)30 << (int32_t)2;
ctx.write();
- EXPECT_TRUE(End_to_End(sync.tagStr,
- "[aggregation time=30ms,count=2]"));
+ EXPECT_TRUE(
+ End_to_End(sync.tagStr, "[aggregation time=30ms,count=2]"));
}
{
android_log_event_list ctx(sync.tagNo);
ctx << (uint64_t)31570 << (int32_t)911;
ctx.write();
- EXPECT_TRUE(End_to_End(sync.tagStr,
- "[aggregation time=31.57s,count=911]"));
+ EXPECT_TRUE(
+ End_to_End(sync.tagStr, "[aggregation time=31.57s,count=911]"));
}
}
@@ -1518,7 +1589,7 @@
{
android_log_event_list ctx(sync.tagNo);
- ctx << (uint32_t)3221225472; // 3MB, but on purpose overflowed
+ ctx << (uint32_t)3221225472; // 3MB, but on purpose overflowed
ctx.write();
EXPECT_TRUE(End_to_End(sync.tagStr, "current=-1GB"));
}
@@ -1533,12 +1604,13 @@
}
static bool reportedSecurity(const char* command) {
- FILE* fp = popen(command, "r");
+ logcat_define(ctx);
+ FILE* fp = logcat_popen(ctx, command);
if (!fp) return true;
std::string ret;
bool val = android::base::ReadFdToString(fileno(fp), &ret);
- pclose(fp);
+ logcat_pclose(ctx, fp);
if (!val) return true;
return std::string::npos != ret.find("'security'");
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 74e0ea5..6ad7351 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -301,6 +301,7 @@
const char *name = NULL;
const char *format = NULL;
+ const char *id = NULL;
for (int i = 1; i < argc; ++i) {
static const char _name[] = "name=";
if (!strncmp(argv[i], _name, strlen(_name))) {
@@ -313,6 +314,21 @@
format = argv[i] + strlen(_format);
continue;
}
+
+ static const char _id[] = "id=";
+ if (!strncmp(argv[i], _id, strlen(_id))) {
+ id = argv[i] + strlen(_id);
+ continue;
+ }
+ }
+
+ if (id) {
+ if (format || name) {
+ cli->sendMsg("can not mix id= with either format= or name=");
+ return 0;
+ }
+ cli->sendMsg(package_string(mBuf.formatEntry(atoi(id), uid)).c_str());
+ return 0;
}
cli->sendMsg(package_string(mBuf.formatGetEventTag(uid,
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index da63e12..9feef32 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -140,6 +140,9 @@
const char *name, const char *format) {
return tags.formatGetEventTag(uid, name, format);
}
+ std::string formatEntry(uint32_t tag, uid_t uid) {
+ return tags.formatEntry(tag, uid);
+ }
const char *tagToName(uint32_t tag) { return tags.tagToName(tag); }
// helper must be protected directly or implicitly by lock()/unlock()
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index a109592..64aa219 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -836,6 +836,11 @@
return ret;
}
+std::string LogTags::formatEntry(uint32_t tag, uid_t uid) {
+ android::RWLock::AutoRLock readLock(rwlock);
+ return formatEntry_locked(tag, uid);
+}
+
std::string LogTags::formatGetEventTag(uid_t uid,
const char* name, const char* format) {
bool all = name && (name[0] == '*') && !name[1];
diff --git a/logd/LogTags.h b/logd/LogTags.h
index 37a6d96..4457c46 100644
--- a/logd/LogTags.h
+++ b/logd/LogTags.h
@@ -106,6 +106,7 @@
// reverse lookup from tag
const char* tagToName(uint32_t tag) const;
const char* tagToFormat(uint32_t tag) const;
+ std::string formatEntry(uint32_t tag, uid_t uid);
// find associated tag
uint32_t nameToTag(const char* name) const;
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index adf583b..8a35059 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -836,6 +836,23 @@
#endif
}
+TEST(logd, getEventTag_42) {
+#ifdef __ANDROID__
+ char buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ snprintf(buffer, sizeof(buffer), "getEventTag id=42");
+ send_to_control(buffer, sizeof(buffer));
+ buffer[sizeof(buffer) - 1] = '\0';
+ char *cp;
+ long ret = strtol(buffer, &cp, 10);
+ EXPECT_GT(ret, 16);
+ EXPECT_TRUE(strstr(buffer, "\t(to life the universe etc|3)") != NULL);
+ EXPECT_TRUE(strstr(buffer, "answer") != NULL);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
TEST(logd, getEventTag_newentry) {
#ifdef __ANDROID__
char buffer[256];
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 3b64f6d..0633a68 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -46,7 +46,7 @@
/dev/tty0 0660 root system
/dev/graphics/* 0660 root graphics
/dev/msm_hw3dm 0660 system graphics
-/dev/input/* 0640 system input
+/dev/input/* 0660 root input
/dev/eac 0660 root audio
/dev/cam 0660 root camera
/dev/pmem 0660 system graphics
diff --git a/toolbox/getevent.c b/toolbox/getevent.c
index 1fb315c..e6def6b 100644
--- a/toolbox/getevent.c
+++ b/toolbox/getevent.c
@@ -321,7 +321,7 @@
char idstr[80];
struct input_id id;
- fd = open(device, O_RDONLY);
+ fd = open(device, O_RDWR);
if(fd < 0) {
if(print_flags & PRINT_DEVICE_ERRORS)
fprintf(stderr, "could not open %s, %s\n", device, strerror(errno));