Merge "logd: liblog: logcat: enable prune features for user"
diff --git a/include/backtrace/BacktraceMap.h b/include/backtrace/BacktraceMap.h
index 06da2f4..13083bd 100644
--- a/include/backtrace/BacktraceMap.h
+++ b/include/backtrace/BacktraceMap.h
@@ -45,7 +45,7 @@
virtual ~BacktraceMap();
// Get the map data structure for the given address.
- const backtrace_map_t* Find(uintptr_t addr);
+ virtual const backtrace_map_t* Find(uintptr_t addr);
// The flags returned are the same flags as used by the mmap call.
// The values are PROT_*.
diff --git a/include/utils/Unicode.h b/include/utils/Unicode.h
index c8c87c3..5b98de2 100644
--- a/include/utils/Unicode.h
+++ b/include/utils/Unicode.h
@@ -22,8 +22,11 @@
extern "C" {
+// Definitions exist in C++11
+#if defined __cplusplus && __cplusplus < 201103L
typedef uint32_t char32_t;
typedef uint16_t char16_t;
+#endif
// Standard string functions on char16_t strings.
int strcmp16(const char16_t *, const char16_t *);
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
index 2e56756..c743077 100755
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -129,9 +129,11 @@
backtrace_test_cflags_target := \
-DGTEST_OS_LINUX_ANDROID \
+ -DENABLE_PSS_TESTS \
backtrace_test_src_files := \
backtrace_test.cpp \
+ GetPss.cpp \
thread_utils.c \
backtrace_test_ldlibs := \
diff --git a/libbacktrace/BacktraceImpl.cpp b/libbacktrace/BacktraceImpl.cpp
index 855810e..05007d9 100644
--- a/libbacktrace/BacktraceImpl.cpp
+++ b/libbacktrace/BacktraceImpl.cpp
@@ -27,6 +27,7 @@
#include <backtrace/BacktraceMap.h>
#include "BacktraceImpl.h"
+#include "BacktraceLog.h"
#include "thread_utils.h"
//-------------------------------------------------------------------------
diff --git a/libbacktrace/BacktraceImpl.h b/libbacktrace/BacktraceImpl.h
index 48dd11c..7b31c38 100755
--- a/libbacktrace/BacktraceImpl.h
+++ b/libbacktrace/BacktraceImpl.h
@@ -21,11 +21,6 @@
#include <backtrace/BacktraceMap.h>
#include <sys/types.h>
-#include <log/log.h>
-
-// Macro to log the function name along with the warning message.
-#define BACK_LOGW(format, ...) \
- ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__)
class BacktraceImpl {
public:
diff --git a/libbacktrace/BacktraceLog.h b/libbacktrace/BacktraceLog.h
new file mode 100755
index 0000000..1632ec2
--- /dev/null
+++ b/libbacktrace/BacktraceLog.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_LOG_H
+#define _LIBBACKTRACE_BACKTRACE_LOG_H
+
+#define LOG_TAG "libbacktrace"
+
+#include <log/log.h>
+
+// Macro to log the function name along with the warning message.
+#define BACK_LOGW(format, ...) \
+ ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__)
+
+#endif // _LIBBACKTRACE_BACKTRACE_LOG_H
diff --git a/libbacktrace/BacktraceThread.cpp b/libbacktrace/BacktraceThread.cpp
index 5ffe516..4cda19e 100644
--- a/libbacktrace/BacktraceThread.cpp
+++ b/libbacktrace/BacktraceThread.cpp
@@ -23,6 +23,7 @@
#include <cutils/atomic.h>
+#include "BacktraceLog.h"
#include "BacktraceThread.h"
#include "thread_utils.h"
diff --git a/libbacktrace/Corkscrew.cpp b/libbacktrace/Corkscrew.cpp
index efeee2e..773b0a2 100644
--- a/libbacktrace/Corkscrew.cpp
+++ b/libbacktrace/Corkscrew.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "libbacktrace"
-
#include <backtrace/Backtrace.h>
#include <string.h>
@@ -28,6 +26,7 @@
#endif
#include <dlfcn.h>
+#include "BacktraceLog.h"
#include "Corkscrew.h"
//-------------------------------------------------------------------------
diff --git a/libbacktrace/GetPss.cpp b/libbacktrace/GetPss.cpp
new file mode 100644
index 0000000..442383b
--- /dev/null
+++ b/libbacktrace/GetPss.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+// This is an extremely simplified version of libpagemap.
+
+#define _BITS(x, offset, bits) (((x) >> offset) & ((1LL << (bits)) - 1))
+
+#define PAGEMAP_PRESENT(x) (_BITS(x, 63, 1))
+#define PAGEMAP_SWAPPED(x) (_BITS(x, 62, 1))
+#define PAGEMAP_SHIFT(x) (_BITS(x, 55, 6))
+#define PAGEMAP_PFN(x) (_BITS(x, 0, 55))
+#define PAGEMAP_SWAP_OFFSET(x) (_BITS(x, 5, 50))
+#define PAGEMAP_SWAP_TYPE(x) (_BITS(x, 0, 5))
+
+static bool ReadData(int fd, unsigned long place, uint64_t *data) {
+ if (lseek(fd, place * sizeof(uint64_t), SEEK_SET) < 0) {
+ return false;
+ }
+ if (read(fd, (void*)data, sizeof(uint64_t)) != (ssize_t)sizeof(uint64_t)) {
+ return false;
+ }
+ return true;
+}
+
+size_t GetPssBytes() {
+ FILE* maps = fopen("/proc/self/maps", "r");
+ assert(maps != NULL);
+
+ int pagecount_fd = open("/proc/kpagecount", O_RDONLY);
+ assert(pagecount_fd >= 0);
+
+ int pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
+ assert(pagemap_fd >= 0);
+
+ char line[4096];
+ size_t total_pss = 0;
+ int pagesize = getpagesize();
+ while (fgets(line, sizeof(line), maps)) {
+ uintptr_t start, end;
+ if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " ", &start, &end) != 2) {
+ total_pss = 0;
+ break;
+ }
+ for (size_t page = start/pagesize; page < end/pagesize; page++) {
+ uint64_t data;
+ if (ReadData(pagemap_fd, page, &data)) {
+ if (PAGEMAP_PRESENT(data) && !PAGEMAP_SWAPPED(data)) {
+ uint64_t count;
+ if (ReadData(pagecount_fd, PAGEMAP_PFN(data), &count)) {
+ total_pss += (count >= 1) ? pagesize / count : 0;
+ }
+ }
+ }
+ }
+ }
+
+ fclose(maps);
+
+ close(pagecount_fd);
+ close(pagemap_fd);
+
+ return total_pss;
+}
diff --git a/libbacktrace/GetPss.h b/libbacktrace/GetPss.h
new file mode 100644
index 0000000..787c33d
--- /dev/null
+++ b/libbacktrace/GetPss.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_GET_PSS_H
+#define _LIBBACKTRACE_GET_PSS_H
+
+size_t GetPssBytes();
+
+#endif // _LIBBACKTRACE_GET_PSS_H
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
index 81e69bb..034b73c 100755
--- a/libbacktrace/UnwindCurrent.cpp
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "libbacktrace"
-
#include <sys/ucontext.h>
#include <sys/types.h>
@@ -25,6 +23,7 @@
#define UNW_LOCAL_ONLY
#include <libunwind.h>
+#include "BacktraceLog.h"
#include "UnwindCurrent.h"
#include "UnwindMap.h"
diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp
index 8268db6..1615518 100644
--- a/libbacktrace/UnwindMap.cpp
+++ b/libbacktrace/UnwindMap.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "libbacktrace"
-
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
@@ -24,6 +22,7 @@
#include <libunwind.h>
+#include "BacktraceLog.h"
#include "UnwindMap.h"
//-------------------------------------------------------------------------
@@ -32,57 +31,21 @@
// only update the local address space once, and keep a reference count
// of maps using the same map cursor.
//-------------------------------------------------------------------------
-static pthread_mutex_t g_map_mutex = PTHREAD_MUTEX_INITIALIZER;
-static unw_map_cursor_t g_map_cursor;
-static int g_map_references = 0;
-
UnwindMap::UnwindMap(pid_t pid) : BacktraceMap(pid) {
- map_cursor_.map_list = NULL;
}
UnwindMap::~UnwindMap() {
- if (pid_ == getpid()) {
- pthread_mutex_lock(&g_map_mutex);
- if (--g_map_references == 0) {
- // Clear the local address space map.
- unw_map_local_set(NULL);
- unw_map_cursor_destroy(&map_cursor_);
- }
- pthread_mutex_unlock(&g_map_mutex);
- } else {
- unw_map_cursor_destroy(&map_cursor_);
- }
+ unw_map_cursor_destroy(&map_cursor_);
+ unw_map_cursor_clear(&map_cursor_);
}
-bool UnwindMap::Build() {
- bool return_value = true;
- if (pid_ == getpid()) {
- pthread_mutex_lock(&g_map_mutex);
- if (g_map_references == 0) {
- return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0);
- if (return_value) {
- // Set the local address space map to our new map.
- unw_map_local_set(&map_cursor_);
- g_map_references = 1;
- g_map_cursor = map_cursor_;
- }
- } else {
- g_map_references++;
- map_cursor_ = g_map_cursor;
- }
- pthread_mutex_unlock(&g_map_mutex);
- } else {
- return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0);
- }
-
- if (!return_value)
- return false;
-
+bool UnwindMap::GenerateMap() {
// Use the map_cursor information to construct the BacktraceMap data
// rather than reparsing /proc/self/maps.
unw_map_cursor_reset(&map_cursor_);
+
unw_map_t unw_map;
- while (unw_map_cursor_get(&map_cursor_, &unw_map)) {
+ while (unw_map_cursor_get_next(&map_cursor_, &unw_map)) {
backtrace_map_t map;
map.start = unw_map.start;
@@ -97,11 +60,82 @@
return true;
}
+bool UnwindMap::Build() {
+ return (unw_map_cursor_create(&map_cursor_, pid_) == 0) && GenerateMap();
+}
+
+UnwindMapLocal::UnwindMapLocal() : UnwindMap(getpid()), map_created_(false) {
+}
+
+UnwindMapLocal::~UnwindMapLocal() {
+ if (map_created_) {
+ unw_map_local_destroy();
+ unw_map_cursor_clear(&map_cursor_);
+ }
+}
+
+bool UnwindMapLocal::GenerateMap() {
+ // It's possible for the map to be regenerated while this loop is occurring.
+ // If that happens, get the map again, but only try at most three times
+ // before giving up.
+ for (int i = 0; i < 3; i++) {
+ maps_.clear();
+
+ unw_map_local_cursor_get(&map_cursor_);
+
+ unw_map_t unw_map;
+ int ret;
+ while ((ret = unw_map_local_cursor_get_next(&map_cursor_, &unw_map)) > 0) {
+ backtrace_map_t map;
+
+ map.start = unw_map.start;
+ map.end = unw_map.end;
+ map.flags = unw_map.flags;
+ map.name = unw_map.path;
+
+ free(unw_map.path);
+
+ // The maps are in descending order, but we want them in ascending order.
+ maps_.push_front(map);
+ }
+ // Check to see if the map changed while getting the data.
+ if (ret != -UNW_EINVAL) {
+ return true;
+ }
+ }
+
+ BACK_LOGW("Unable to generate the map.");
+ return false;
+}
+
+bool UnwindMapLocal::Build() {
+ return (map_created_ = (unw_map_local_create() == 0)) && GenerateMap();;
+}
+
+const backtrace_map_t* UnwindMapLocal::Find(uintptr_t addr) {
+ const backtrace_map_t* map = BacktraceMap::Find(addr);
+ if (!map) {
+ // Check to see if the underlying map changed and regenerate the map
+ // if it did.
+ if (unw_map_local_cursor_valid(&map_cursor_) < 0) {
+ if (GenerateMap()) {
+ map = BacktraceMap::Find(addr);
+ }
+ }
+ }
+ return map;
+}
+
//-------------------------------------------------------------------------
// BacktraceMap create function.
//-------------------------------------------------------------------------
BacktraceMap* BacktraceMap::Create(pid_t pid) {
- BacktraceMap* map = new UnwindMap(pid);
+ BacktraceMap* map;
+ if (pid == getpid()) {
+ map = new UnwindMapLocal();
+ } else {
+ map = new UnwindMap(pid);
+ }
if (!map->Build()) {
delete map;
return NULL;
diff --git a/libbacktrace/UnwindMap.h b/libbacktrace/UnwindMap.h
index 5a874e8..2fdb29f 100644
--- a/libbacktrace/UnwindMap.h
+++ b/libbacktrace/UnwindMap.h
@@ -32,8 +32,25 @@
unw_map_cursor_t* GetMapCursor() { return &map_cursor_; }
-private:
+protected:
+ virtual bool GenerateMap();
+
unw_map_cursor_t map_cursor_;
};
+class UnwindMapLocal : public UnwindMap {
+public:
+ UnwindMapLocal();
+ virtual ~UnwindMapLocal();
+
+ virtual bool Build();
+
+ virtual const backtrace_map_t* Find(uintptr_t addr);
+
+protected:
+ virtual bool GenerateMap();
+
+ bool map_created_;
+};
+
#endif // _LIBBACKTRACE_UNWIND_MAP_H
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp
index 732dae8..5ca7e60 100644
--- a/libbacktrace/UnwindPtrace.cpp
+++ b/libbacktrace/UnwindPtrace.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "libbacktrace"
-
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
@@ -25,6 +23,7 @@
#include <libunwind.h>
#include <libunwind-ptrace.h>
+#include "BacktraceLog.h"
#include "UnwindMap.h"
#include "UnwindPtrace.h"
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 23eaf92..a5e141b 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -16,9 +16,10 @@
#include <dirent.h>
#include <errno.h>
+#include <inttypes.h>
#include <pthread.h>
#include <signal.h>
-#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -35,6 +36,7 @@
#include <cutils/atomic.h>
#include <gtest/gtest.h>
+#include <algorithm>
#include <vector>
#include "thread_utils.h"
@@ -287,7 +289,7 @@
pid_t pid;
if ((pid = fork()) == 0) {
ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
- exit(1);
+ _exit(1);
}
VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyLevelDump);
@@ -300,7 +302,7 @@
pid_t pid;
if ((pid = fork()) == 0) {
ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
- exit(1);
+ _exit(1);
}
VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, true, ReadyLevelBacktrace, VerifyLevelDump);
@@ -314,7 +316,7 @@
pid_t pid;
if ((pid = fork()) == 0) {
ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, NULL, NULL), 0);
- exit(1);
+ _exit(1);
}
VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyMaxBacktrace, VerifyMaxDump);
@@ -339,7 +341,7 @@
pid_t pid;
if ((pid = fork()) == 0) {
ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
- exit(1);
+ _exit(1);
}
VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyProcessIgnoreFrames);
@@ -384,7 +386,7 @@
ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, NULL) == 0);
}
ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
- exit(1);
+ _exit(1);
}
// Check to see that all of the threads are running before unwinding.
@@ -693,3 +695,136 @@
#endif
backtrace->FormatFrameData(&frame));
}
+
+struct map_test_t {
+ uintptr_t start;
+ uintptr_t end;
+};
+
+bool map_sort(map_test_t i, map_test_t j) {
+ return i.start < j.start;
+}
+
+static void VerifyMap(pid_t pid) {
+ char buffer[4096];
+ snprintf(buffer, sizeof(buffer), "/proc/%d/maps", pid);
+
+ FILE* map_file = fopen(buffer, "r");
+ ASSERT_TRUE(map_file != NULL);
+ std::vector<map_test_t> test_maps;
+ while (fgets(buffer, sizeof(buffer), map_file)) {
+ map_test_t map;
+ ASSERT_EQ(2, sscanf(buffer, "%" SCNxPTR "-%" SCNxPTR " ", &map.start, &map.end));
+ test_maps.push_back(map);
+ }
+ fclose(map_file);
+ std::sort(test_maps.begin(), test_maps.end(), map_sort);
+
+ UniquePtr<BacktraceMap> map(BacktraceMap::Create(pid));
+
+ // Basic test that verifies that the map is in the expected order.
+ std::vector<map_test_t>::const_iterator test_it = test_maps.begin();
+ for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) {
+ ASSERT_TRUE(test_it != test_maps.end());
+ ASSERT_EQ(test_it->start, it->start);
+ ASSERT_EQ(test_it->end, it->end);
+ ++test_it;
+ }
+ ASSERT_TRUE(test_it == test_maps.end());
+}
+
+TEST(libbacktrace, verify_map_remote) {
+ pid_t pid;
+
+ if ((pid = fork()) == 0) {
+ while (true) {
+ }
+ _exit(0);
+ }
+ ASSERT_LT(0, pid);
+
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+
+ // Wait for the process to get to a stopping point.
+ WaitForStop(pid);
+
+ // The maps should match exactly since the forked process has been paused.
+ VerifyMap(pid);
+
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ kill(pid, SIGKILL);
+ ASSERT_EQ(waitpid(pid, NULL, 0), pid);
+}
+
+#if defined(ENABLE_PSS_TESTS)
+#include "GetPss.h"
+
+#define MAX_LEAK_BYTES 32*1024UL
+
+static void CheckForLeak(pid_t pid, pid_t tid) {
+ // Do a few runs to get the PSS stable.
+ for (size_t i = 0; i < 100; i++) {
+ Backtrace* backtrace = Backtrace::Create(pid, tid);
+ ASSERT_TRUE(backtrace != NULL);
+ ASSERT_TRUE(backtrace->Unwind(0));
+ delete backtrace;
+ }
+ size_t stable_pss = GetPssBytes();
+
+ // Loop enough that even a small leak should be detectable.
+ for (size_t i = 0; i < 4096; i++) {
+ Backtrace* backtrace = Backtrace::Create(pid, tid);
+ ASSERT_TRUE(backtrace != NULL);
+ ASSERT_TRUE(backtrace->Unwind(0));
+ delete backtrace;
+ }
+ size_t new_pss = GetPssBytes();
+ size_t abs_diff = (new_pss > stable_pss) ? new_pss - stable_pss : stable_pss - new_pss;
+ // As long as the new pss is within a certain amount, consider everything okay.
+ ASSERT_LE(abs_diff, MAX_LEAK_BYTES);
+}
+
+TEST(libbacktrace, check_for_leak_local) {
+ CheckForLeak(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD);
+}
+
+TEST(libbacktrace, check_for_leak_local_thread) {
+ thread_t thread_data = { 0, 0, 0 };
+ pthread_t thread;
+ ASSERT_TRUE(pthread_create(&thread, NULL, ThreadLevelRun, &thread_data) == 0);
+
+ // Wait up to 2 seconds for the tid to be set.
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
+
+ CheckForLeak(BACKTRACE_CURRENT_PROCESS, thread_data.tid);
+
+ // Tell the thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &thread_data.state);
+
+ ASSERT_TRUE(pthread_join(thread, NULL) == 0);
+}
+
+TEST(libbacktrace, check_for_leak_remote) {
+ pid_t pid;
+
+ if ((pid = fork()) == 0) {
+ while (true) {
+ }
+ _exit(0);
+ }
+ ASSERT_LT(0, pid);
+
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+
+ // Wait for the process to get to a stopping point.
+ WaitForStop(pid);
+
+ CheckForLeak(pid, BACKTRACE_CURRENT_THREAD);
+
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ kill(pid, SIGKILL);
+ ASSERT_EQ(waitpid(pid, NULL, 0), pid);
+}
+#endif
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index 93bccb0..635695a 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -91,6 +91,16 @@
LOCAL_CFLAGS += $(hostSmpFlag) -m64
include $(BUILD_HOST_STATIC_LIBRARY)
+# Tests for host
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := tst_str_parms
+LOCAL_CFLAGS += -DTEST_STR_PARMS
+LOCAL_SRC_FILES := str_parms.c hashmap.c memory.c
+LOCAL_STATIC_LIBRARIES := liblog
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_HOST_EXECUTABLE)
+
# Shared and static library for target
# ========================================================
diff --git a/libcutils/str_parms.c b/libcutils/str_parms.c
index 7cfbcb3..953b91d 100644
--- a/libcutils/str_parms.c
+++ b/libcutils/str_parms.c
@@ -194,23 +194,46 @@
int str_parms_add_str(struct str_parms *str_parms, const char *key,
const char *value)
{
- void *old_val;
- void *tmp_key;
- void *tmp_val;
+ void *tmp_key = NULL;
+ void *tmp_val = NULL;
+ void *old_val = NULL;
+
+ // strdup and hashmapPut both set errno on failure.
+ // Set errno to 0 so we can recognize whether anything went wrong.
+ int saved_errno = errno;
+ errno = 0;
tmp_key = strdup(key);
- tmp_val = strdup(value);
- old_val = hashmapPut(str_parms->map, tmp_key, tmp_val);
-
- if (old_val) {
- free(old_val);
- free(tmp_key);
- } else if (errno == ENOMEM) {
- free(tmp_key);
- free(tmp_val);
- return -ENOMEM;
+ if (tmp_key == NULL) {
+ goto clean_up;
}
- return 0;
+
+ tmp_val = strdup(value);
+ if (tmp_val == NULL) {
+ goto clean_up;
+ }
+
+ old_val = hashmapPut(str_parms->map, tmp_key, tmp_val);
+ if (old_val == NULL) {
+ // Did hashmapPut fail?
+ if (errno == ENOMEM) {
+ goto clean_up;
+ }
+ // For new keys, hashmap takes ownership of tmp_key and tmp_val.
+ tmp_key = tmp_val = NULL;
+ } else {
+ // For existing keys, hashmap takes ownership of tmp_val.
+ // (It also gives up ownership of old_val.)
+ tmp_val = NULL;
+ }
+
+clean_up:
+ free(tmp_key);
+ free(tmp_val);
+ free(old_val);
+ int result = -errno;
+ errno = saved_errno;
+ return result;
}
int str_parms_add_int(struct str_parms *str_parms, const char *key, int value)
@@ -337,7 +360,6 @@
{
struct str_parms *str_parms;
char *out_str;
- int ret;
str_parms = str_parms_create_str(str);
str_parms_add_str(str_parms, "dude", "woah");
@@ -370,6 +392,15 @@
test_str_parms_str("foo=bar;baz=bat;");
test_str_parms_str("foo=bar;baz=bat;foo=bar");
+ // hashmapPut reports errors by setting errno to ENOMEM.
+ // Test that we're not confused by running in an environment where this is already true.
+ errno = ENOMEM;
+ test_str_parms_str("foo=bar;baz=");
+ if (errno != ENOMEM) {
+ abort();
+ }
+ test_str_parms_str("foo=bar;baz=");
+
return 0;
}
#endif
diff --git a/logd/Android.mk b/logd/Android.mk
index ad747dd..629d4fa 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -15,7 +15,9 @@
LogBufferElement.cpp \
LogTimes.cpp \
LogStatistics.cpp \
- LogWhiteBlackList.cpp
+ LogWhiteBlackList.cpp \
+ libaudit.c \
+ LogAudit.cpp
LOCAL_SHARED_LIBRARIES := \
libsysutils \
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
new file mode 100644
index 0000000..ea6eece
--- /dev/null
+++ b/logd/LogAudit.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/klog.h>
+#include <sys/uio.h>
+
+#include "libaudit.h"
+#include "LogAudit.h"
+
+LogAudit::LogAudit(LogBuffer *buf, LogReader *reader, int fdDmsg)
+ : SocketListener(getLogSocket(), false)
+ , logbuf(buf)
+ , reader(reader)
+ , fdDmesg(-1) {
+ logDmesg();
+ fdDmesg = fdDmsg;
+}
+
+bool LogAudit::onDataAvailable(SocketClient *cli) {
+ struct audit_message rep;
+
+ if (audit_get_reply(cli->getSocket(), &rep, GET_REPLY_BLOCKING, 0) < 0) {
+ SLOGE("Failed on audit_get_reply with error: %s", strerror(errno));
+ return false;
+ }
+
+ logPrint("type=%d %.*s", rep.nlh.nlmsg_type, rep.nlh.nlmsg_len, rep.data);
+
+ return true;
+}
+
+#define AUDIT_LOG_ID LOG_ID_MAIN
+#define AUDIT_LOG_PRIO ANDROID_LOG_WARN
+
+int LogAudit::logPrint(const char *fmt, ...) {
+ if (fmt == NULL) {
+ return -EINVAL;
+ }
+
+ va_list args;
+
+ char *str = NULL;
+ va_start(args, fmt);
+ int rc = vasprintf(&str, fmt, args);
+ va_end(args);
+
+ if (rc < 0) {
+ return rc;
+ }
+
+ if (fdDmesg >= 0) {
+ struct iovec iov[2];
+
+ iov[0].iov_base = str;
+ iov[0].iov_len = strlen(str);
+ iov[1].iov_base = const_cast<char *>("\n");
+ iov[1].iov_len = 1;
+
+ writev(fdDmesg, iov, sizeof(iov) / sizeof(iov[0]));
+ }
+
+ pid_t pid = getpid();
+ pid_t tid = gettid();
+ uid_t uid = getuid();
+ log_time now;
+
+ static const char audit_str[] = " audit(";
+ char *timeptr = strstr(str, audit_str);
+ char *cp;
+ if (timeptr
+ && ((cp = now.strptime(timeptr + sizeof(audit_str) - 1, "%s.%q")))
+ && (*cp == ':')) {
+ memcpy(timeptr + sizeof(audit_str) - 1, "0.0", 3);
+ strcpy(timeptr + sizeof(audit_str) - 1 + 3, cp);
+ } else {
+ now.strptime("", ""); // side effect of setting CLOCK_REALTIME
+ }
+
+ static const char pid_str[] = " pid=";
+ char *pidptr = strstr(str, pid_str);
+ if (pidptr && isdigit(pidptr[sizeof(pid_str) - 1])) {
+ cp = pidptr + sizeof(pid_str) - 1;
+ pid = 0;
+ while (isdigit(*cp)) {
+ pid = (pid * 10) + (*cp - '0');
+ ++cp;
+ }
+ tid = pid;
+ uid = logbuf->pidToUid(pid);
+ strcpy(pidptr, cp);
+ }
+
+ static const char comm_str[] = " comm=\"";
+ char *comm = strstr(str, comm_str);
+ if (comm) {
+ cp = comm;
+ comm += sizeof(comm_str) - 1;
+ char *ecomm = strchr(comm, '"');
+ if (ecomm) {
+ *ecomm = '\0';
+ }
+ comm = strdup(comm);
+ if (ecomm) {
+ strcpy(cp, ecomm + 1);
+ }
+ } else if (pid == getpid()) {
+ pid = tid;
+ comm = strdup("auditd");
+ } else if (!(comm = logbuf->pidToName(pid))) {
+ comm = strdup("unknown");
+ }
+
+ size_t l = strlen(comm) + 1;
+ size_t n = l + strlen(str) + 2;
+
+ char *newstr = reinterpret_cast<char *>(malloc(n));
+ if (!newstr) {
+ free(comm);
+ free(str);
+ return -ENOMEM;
+ }
+
+ *newstr = AUDIT_LOG_PRIO;
+ strcpy(newstr + 1, comm);
+ free(comm);
+ strcpy(newstr + 1 + l, str);
+ free(str);
+
+ unsigned short len = n; // cap to internal maximum
+ if (len != n) {
+ len = -1;
+ }
+ logbuf->log(AUDIT_LOG_ID, now, uid, pid, tid, newstr, len);
+ reader->notifyNewLog();
+
+ free(newstr);
+
+ return rc;
+}
+
+void LogAudit::logDmesg() {
+ int len = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
+ if (len <= 0) {
+ return;
+ }
+
+ len++;
+ char buf[len];
+
+ int rc = klogctl(KLOG_READ_ALL, buf, len);
+
+ buf[len - 1] = '\0';
+
+ for(char *tok = buf; (rc >= 0) && ((tok = strtok(tok, "\r\n"))); tok = NULL) {
+ char *audit = strstr(tok, " audit(");
+ if (!audit) {
+ continue;
+ }
+
+ *audit++ = '\0';
+
+ char *type = strstr(tok, "type=");
+ if (type) {
+ rc = logPrint("%s %s", type, audit);
+ } else {
+ rc = logPrint("%s", audit);
+ }
+ }
+}
+
+int LogAudit::getLogSocket() {
+ int fd = audit_open();
+ if (fd < 0) {
+ return fd;
+ }
+ if (audit_set_pid(fd, getpid(), WAIT_YES) < 0) {
+ audit_close(fd);
+ fd = -1;
+ }
+ return fd;
+}
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
new file mode 100644
index 0000000..111030a
--- /dev/null
+++ b/logd/LogAudit.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_AUDIT_H__
+#define _LOGD_LOG_AUDIT_H__
+
+#include <sysutils/SocketListener.h>
+#include "LogReader.h"
+
+class LogAudit : public SocketListener {
+ LogBuffer *logbuf;
+ LogReader *reader;
+ int fdDmesg;
+
+public:
+ LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg);
+
+protected:
+ virtual bool onDataAvailable(SocketClient *cli);
+
+private:
+ static int getLogSocket();
+ void logDmesg();
+ int logPrint(const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 2, 3)));
+};
+
+#endif
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index dd02009..b8a54b9 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -71,6 +71,10 @@
// *strp uses malloc, use free to release.
void formatPrune(char **strp) { mPrune.format(strp); }
+ // helper
+ char *pidToName(pid_t pid) { return stats.pidToName(pid); }
+ uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
+
private:
void maybePrune(log_id_t id);
void prune(log_id_t id, unsigned long pruneRows);
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 5622ee5..fc6e6b2 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <fcntl.h>
#include <stdarg.h>
#include <time.h>
@@ -23,12 +24,34 @@
#include "LogStatistics.h"
-PidStatistics::PidStatistics(pid_t pid)
+PidStatistics::PidStatistics(pid_t pid, char *name)
: pid(pid)
, mSizesTotal(0)
, mElementsTotal(0)
, mSizes(0)
- , mElements(0) { }
+ , mElements(0)
+ , name(name)
+{ }
+
+#ifdef DO_NOT_ERROR_IF_PIDSTATISTICS_USES_A_COPY_CONSTRUCTOR
+PidStatistics::PidStatistics(const PidStatistics ©)
+ : pid(copy->pid)
+ , name(copy->name ? strdup(copy->name) : NULL)
+ , mSizesTotal(copy->mSizesTotal)
+ , mElementsTotal(copy->mElementsTotal)
+ , mSizes(copy->mSizes)
+ , mElements(copy->mElements)
+{ }
+#endif
+
+PidStatistics::~PidStatistics() {
+ free(name);
+}
+
+void PidStatistics::setName(char *new_name) {
+ free(name);
+ name = new_name;
+}
void PidStatistics::add(unsigned short size) {
mSizesTotal += size;
@@ -50,6 +73,28 @@
}
}
+// must call free to release return value
+char *PidStatistics::pidToName(pid_t pid) {
+ char *retval = NULL;
+ if (pid != PidStatistics::gone) {
+ char buffer[512];
+ snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid);
+ int fd = open(buffer, O_RDONLY);
+ if (fd >= 0) {
+ ssize_t ret = read(fd, buffer, sizeof(buffer));
+ if (ret > 0) {
+ buffer[sizeof(buffer)-1] = '\0';
+ // frameworks intermediate state
+ if (strcmp(buffer, "<pre-initialized>")) {
+ retval = strdup(buffer);
+ }
+ }
+ close(fd);
+ }
+ }
+ return retval;
+}
+
UidStatistics::UidStatistics(uid_t uid)
: uid(uid) {
Pids.clear();
@@ -83,7 +128,7 @@
bool insert = (last != it)
&& ((p->getPid() == p->gone)
|| ((*last)->sizesTotal() < (size_t) size));
- p = new PidStatistics(pid);
+ p = new PidStatistics(pid, pidToName(pid));
if (insert) {
Pids.insert(last, p);
} else {
@@ -397,11 +442,11 @@
void LogStatistics::format(char **buf,
uid_t uid, unsigned int logMask, log_time oldest) {
- const unsigned short spaces_current = 13;
- const unsigned short spaces_total = 19;
+ static const unsigned short spaces_current = 13;
+ static const unsigned short spaces_total = 19;
if (*buf) {
- free(buf);
+ free(*buf);
*buf = NULL;
}
@@ -683,3 +728,20 @@
*buf = strdup(string.string());
}
+
+uid_t LogStatistics::pidToUid(pid_t pid) {
+ log_id_for_each(i) {
+ LidStatistics &l = id(i);
+ UidStatisticsCollection::iterator iu;
+ for (iu = l.begin(); iu != l.end(); ++iu) {
+ UidStatistics &u = *(*iu);
+ PidStatisticsCollection::iterator ip;
+ for (ip = u.begin(); ip != u.end(); ++ip) {
+ if ((*ip)->getPid() == pid) {
+ return u.getUid();
+ }
+ }
+ }
+ }
+ return getuid(); // associate this with the logger
+}
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 6a26263..12c68d5 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -36,12 +36,18 @@
size_t mSizes;
size_t mElements;
+ char *name;
+
public:
static const pid_t gone = (pid_t) -1;
- PidStatistics(pid_t pid);
+ PidStatistics(pid_t pid, char *name = NULL);
+ PidStatistics(const PidStatistics ©);
+ ~PidStatistics();
pid_t getPid() const { return pid; }
+ char *getName() const { return name; }
+ void setName(char *name);
void add(unsigned short size);
bool subtract(unsigned short size); // returns true if stats and PID gone
@@ -52,6 +58,9 @@
size_t sizesTotal() const { return mSizesTotal; }
size_t elementsTotal() const { return mElementsTotal; }
+
+ // helper
+ static char *pidToName(pid_t pid);
};
typedef android::List<PidStatistics *> PidStatisticsCollection;
@@ -80,6 +89,9 @@
size_t sizesTotal(pid_t pid = pid_all);
size_t elementsTotal(pid_t pid = pid_all);
+
+ // helper
+ static char *pidToName(pid_t pid) { return PidStatistics::pidToName(pid); }
};
typedef android::List<UidStatistics *> UidStatisticsCollection;
@@ -157,6 +169,10 @@
// *strp = malloc, balance with free
void format(char **strp, uid_t uid, unsigned int logMask, log_time oldest);
+
+ // helper
+ static char *pidToName(pid_t pid) { return PidStatistics::pidToName(pid); }
+ uid_t pidToUid(pid_t pid);
};
#endif // _LOGD_LOG_STATISTICS_H__
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index 8cb015c..c32ac2d 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -46,14 +46,25 @@
{ }
void LogTimeEntry::startReader_Locked(void) {
+ pthread_attr_t attr;
+
threadRunning = true;
- if (pthread_create(&mThread, NULL, LogTimeEntry::threadStart, this)) {
- threadRunning = false;
- if (mClient) {
- mClient->decRef();
+
+ if (!pthread_attr_init(&attr)) {
+ if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
+ if (!pthread_create(&mThread, &attr,
+ LogTimeEntry::threadStart, this)) {
+ pthread_attr_destroy(&attr);
+ return;
+ }
}
- decRef_Locked();
+ pthread_attr_destroy(&attr);
}
+ threadRunning = false;
+ if (mClient) {
+ mClient->decRef();
+ }
+ decRef_Locked();
}
void LogTimeEntry::threadStop(void *obj) {
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
index c6c7b23..f739865 100644
--- a/logd/LogWhiteBlackList.cpp
+++ b/logd/LogWhiteBlackList.cpp
@@ -47,7 +47,7 @@
}
PruneList::PruneList()
- : mWorstUidEnabled(true) {
+ : mWorstUidEnabled(false) {
mNaughty.clear();
mNice.clear();
}
diff --git a/logd/README.auditd b/logd/README.auditd
new file mode 100644
index 0000000..3f614a3
--- /dev/null
+++ b/logd/README.auditd
@@ -0,0 +1,17 @@
+Auditd Daemon
+
+The audit daemon is a simplified version of its desktop
+counterpart designed to gather the audit logs from the
+audit kernel subsystem. The audit subsystem of the kernel
+includes Linux Security Modules (LSM) messages as well.
+
+To enable the audit subsystem, you must add this to your
+kernel config:
+CONFIG_AUDIT=y
+
+To enable a LSM, you must consult that LSM's documentation, the
+example below is for SELinux:
+CONFIG_SECURITY_SELINUX=y
+
+This does not include possible dependencies that may need to be
+satisfied for that particular LSM.
diff --git a/logd/libaudit.c b/logd/libaudit.c
new file mode 100644
index 0000000..ca88d1b
--- /dev/null
+++ b/logd/libaudit.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2012, Samsung Telecommunications of America
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Written by William Roberts <w.roberts@sta.samsung.com>
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#define LOG_TAG "libaudit"
+#include <log/log.h>
+
+#include "libaudit.h"
+
+/**
+ * Waits for an ack from the kernel
+ * @param fd
+ * The netlink socket fd
+ * @param seq
+ * The current sequence number were acking on
+ * @return
+ * This function returns 0 on success, else -errno.
+ */
+static int get_ack(int fd, int16_t seq)
+{
+ int rc;
+ struct audit_message rep;
+
+ /* Sanity check, this is an internal interface this shouldn't happen */
+ if (fd < 0) {
+ return -EINVAL;
+ }
+
+ rc = audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, MSG_PEEK);
+ if (rc < 0) {
+ return rc;
+ }
+
+ if (rep.nlh.nlmsg_type == NLMSG_ERROR) {
+ audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, 0);
+ rc = ((struct nlmsgerr *)rep.data)->error;
+ if (rc) {
+ return -rc;
+ }
+ }
+
+ if ((int16_t)rep.nlh.nlmsg_seq != seq) {
+ SLOGW("Expected sequence number between user space and kernel space is out of skew, "
+ "expected %u got %u", seq, rep.nlh.nlmsg_seq);
+ }
+
+ return 0;
+}
+
+/**
+ *
+ * @param fd
+ * The netlink socket fd
+ * @param type
+ * The type of netlink message
+ * @param data
+ * The data to send
+ * @param size
+ * The length of the data in bytes
+ * @return
+ * This function returns a positive sequence number on success, else -errno.
+ */
+static int audit_send(int fd, int type, const void *data, size_t size)
+{
+ int rc;
+ static int16_t sequence = 0;
+ struct audit_message req;
+ struct sockaddr_nl addr;
+
+ memset(&req, 0, sizeof(req));
+ memset(&addr, 0, sizeof(addr));
+
+ /* We always send netlink messaged */
+ addr.nl_family = AF_NETLINK;
+
+ /* Set up the netlink headers */
+ req.nlh.nlmsg_type = type;
+ req.nlh.nlmsg_len = NLMSG_SPACE(size);
+ req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+
+ /*
+ * Check for a valid fd, even though sendto would catch this, its easier
+ * to always blindly increment the sequence number
+ */
+ if (fd < 0) {
+ return -EBADF;
+ }
+
+ /* Ensure the message is not too big */
+ if (NLMSG_SPACE(size) > MAX_AUDIT_MESSAGE_LENGTH) {
+ SLOGE("netlink message is too large");
+ return -EINVAL;
+ }
+
+ /* Only memcpy in the data if it was specified */
+ if (size && data) {
+ memcpy(NLMSG_DATA(&req.nlh), data, size);
+ }
+
+ /*
+ * Only increment the sequence number on a guarantee
+ * you will send it to the kernel.
+ *
+ * Also, the sequence is defined as a u32 in the kernel
+ * struct. Using an int here might not work on 32/64 bit splits. A
+ * signed 64 bit value can overflow a u32..but a u32
+ * might not fit in the response, so we need to use s32.
+ * Which is still kind of hackish since int could be 16 bits
+ * in size. The only safe type to use here is a signed 16
+ * bit value.
+ */
+ req.nlh.nlmsg_seq = ++sequence;
+
+ /* While failing and its due to interrupts */
+
+ rc = TEMP_FAILURE_RETRY(sendto(fd, &req, req.nlh.nlmsg_len, 0,
+ (struct sockaddr*) &addr, sizeof(addr)));
+
+ /* Not all the bytes were sent */
+ if (rc < 0) {
+ rc = -errno;
+ SLOGE("Error sending data over the netlink socket: %s", strerror(-errno));
+ goto out;
+ } else if ((uint32_t) rc != req.nlh.nlmsg_len) {
+ rc = -EPROTO;
+ goto out;
+ }
+
+ /* We sent all the bytes, get the ack */
+ rc = get_ack(fd, sequence);
+
+ /* If the ack failed, return the error, else return the sequence number */
+ rc = (rc == 0) ? (int) sequence : rc;
+
+out:
+ /* Don't let sequence roll to negative */
+ if (sequence < 0) {
+ SLOGW("Auditd to Kernel sequence number has rolled over");
+ sequence = 0;
+ }
+
+ return rc;
+}
+
+int audit_set_pid(int fd, uint32_t pid, rep_wait_t wmode)
+{
+ int rc;
+ struct audit_message rep;
+ struct audit_status status;
+
+ memset(&status, 0, sizeof(status));
+
+ /*
+ * In order to set the auditd PID we send an audit message over the netlink
+ * socket with the pid field of the status struct set to our current pid,
+ * and the the mask set to AUDIT_STATUS_PID
+ */
+ status.pid = pid;
+ status.mask = AUDIT_STATUS_PID;
+
+ /* Let the kernel know this pid will be registering for audit events */
+ rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
+ if (rc < 0) {
+ SLOGE("Could net set pid for audit events, error: %s", strerror(-rc));
+ return rc;
+ }
+
+ /*
+ * In a request where we need to wait for a response, wait for the message
+ * and discard it. This message confirms and sync's us with the kernel.
+ * This daemon is now registered as the audit logger. Only wait if the
+ * wmode is != WAIT_NO
+ */
+ if (wmode != WAIT_NO) {
+ /* TODO
+ * If the daemon dies and restarts the message didn't come back,
+ * so I went to non-blocking and it seemed to fix the bug.
+ * Need to investigate further.
+ */
+ audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0);
+ }
+
+ return 0;
+}
+
+int audit_open()
+{
+ return socket(PF_NETLINK, SOCK_RAW, NETLINK_AUDIT);
+}
+
+int audit_get_reply(int fd, struct audit_message *rep, reply_t block, int peek)
+{
+ ssize_t len;
+ int flags;
+ int rc = 0;
+
+ struct sockaddr_nl nladdr;
+ socklen_t nladdrlen = sizeof(nladdr);
+
+ if (fd < 0) {
+ return -EBADF;
+ }
+
+ /* Set up the flags for recv from */
+ flags = (block == GET_REPLY_NONBLOCKING) ? MSG_DONTWAIT : 0;
+ flags |= peek;
+
+ /*
+ * Get the data from the netlink socket but on error we need to be carefull,
+ * the interface shows that EINTR can never be returned, other errors,
+ * however, can be returned.
+ */
+ len = TEMP_FAILURE_RETRY(recvfrom(fd, rep, sizeof(*rep), flags,
+ (struct sockaddr*) &nladdr, &nladdrlen));
+
+ /*
+ * EAGAIN should be re-tried until success or another error manifests.
+ */
+ if (len < 0) {
+ rc = -errno;
+ if (block == GET_REPLY_NONBLOCKING && rc == -EAGAIN) {
+ /* If request is non blocking and errno is EAGAIN, just return 0 */
+ return 0;
+ }
+ SLOGE("Error receiving from netlink socket, error: %s", strerror(-rc));
+ return rc;
+ }
+
+ if (nladdrlen != sizeof(nladdr)) {
+ SLOGE("Protocol fault, error: %s", strerror(EPROTO));
+ return -EPROTO;
+ }
+
+ /* Make sure the netlink message was not spoof'd */
+ if (nladdr.nl_pid) {
+ SLOGE("Invalid netlink pid received, expected 0 got: %d", nladdr.nl_pid);
+ return -EINVAL;
+ }
+
+ /* Check if the reply from the kernel was ok */
+ if (!NLMSG_OK(&rep->nlh, (size_t)len)) {
+ rc = (len == sizeof(*rep)) ? -EFBIG : -EBADE;
+ SLOGE("Bad kernel response %s", strerror(-rc));
+ }
+
+ return rc;
+}
+
+void audit_close(int fd)
+{
+ int rc = close(fd);
+ if (rc < 0) {
+ SLOGE("Attempting to close invalid fd %d, error: %s", fd, strerror(errno));
+ }
+ return;
+}
diff --git a/logd/libaudit.h b/logd/libaudit.h
new file mode 100644
index 0000000..cb114f9
--- /dev/null
+++ b/logd/libaudit.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2012, Samsung Telecommunications of America
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Written by William Roberts <w.roberts@sta.samsung.com>
+ */
+
+#ifndef _LIBAUDIT_H_
+#define _LIBAUDIT_H_
+
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <linux/netlink.h>
+#include <linux/audit.h>
+
+__BEGIN_DECLS
+
+#define MAX_AUDIT_MESSAGE_LENGTH 8970
+
+typedef enum {
+ GET_REPLY_BLOCKING=0,
+ GET_REPLY_NONBLOCKING
+} reply_t;
+
+typedef enum {
+ WAIT_NO,
+ WAIT_YES
+} rep_wait_t;
+
+/* type == AUDIT_SIGNAL_INFO */
+struct audit_sig_info {
+ uid_t uid;
+ pid_t pid;
+ char ctx[0];
+};
+
+struct audit_message {
+ struct nlmsghdr nlh;
+ char data[MAX_AUDIT_MESSAGE_LENGTH];
+};
+
+/**
+ * Opens a connection to the Audit netlink socket
+ * @return
+ * A valid fd on success or < 0 on error with errno set.
+ * Returns the same errors as man 2 socket.
+ */
+extern int audit_open(void);
+
+/**
+ * Closes the fd returned from audit_open()
+ * @param fd
+ * The fd to close
+ */
+extern void audit_close(int fd);
+
+/**
+ *
+ * @param fd
+ * The fd returned by a call to audit_open()
+ * @param rep
+ * The response struct to store the response in.
+ * @param block
+ * Whether or not to block on IO
+ * @param peek
+ * Whether or not we are to remove the message from
+ * the queue when we do a read on the netlink socket.
+ * @return
+ * This function returns 0 on success, else -errno.
+ */
+extern int audit_get_reply(int fd, struct audit_message *rep, reply_t block,
+ int peek);
+
+/**
+ * Sets a pid to recieve audit netlink events from the kernel
+ * @param fd
+ * The fd returned by a call to audit_open()
+ * @param pid
+ * The pid whom to set as the reciever of audit messages
+ * @param wmode
+ * Whether or not to block on the underlying socket io calls.
+ * @return
+ * This function returns 0 on success, -errno on error.
+ */
+extern int audit_set_pid(int fd, uint32_t pid, rep_wait_t wmode);
+
+__END_DECLS
+
+#endif
diff --git a/logd/main.cpp b/logd/main.cpp
index 8792d32..7346e2f 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -34,6 +34,7 @@
#include "CommandListener.h"
#include "LogBuffer.h"
#include "LogListener.h"
+#include "LogAudit.h"
static int drop_privs() {
struct sched_param param;
@@ -63,7 +64,10 @@
capheader.pid = 0;
capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
- capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
+ capdata[CAP_TO_INDEX(CAP_AUDIT_CONTROL)].permitted |= CAP_TO_MASK(CAP_AUDIT_CONTROL);
+
+ capdata[0].effective = capdata[0].permitted;
+ capdata[1].effective = capdata[1].permitted;
capdata[0].inheritable = 0;
capdata[1].inheritable = 0;
@@ -80,6 +84,13 @@
// space logger. Additional transitory per-client threads are created
// for each reader once they register.
int main() {
+ int fdDmesg = -1;
+ char dmesg[PROPERTY_VALUE_MAX];
+ property_get("logd.auditd.dmesg", dmesg, "1");
+ if (atol(dmesg)) {
+ fdDmesg = open("/dev/kmsg", O_WRONLY);
+ }
+
if (drop_privs() != 0) {
return -1;
}
@@ -127,6 +138,17 @@
exit(1);
}
+ // LogAudit listens on NETLINK_AUDIT socket for selinux
+ // initiated log messages. New log entries are added to LogBuffer
+ // and LogReader is notified to send updates to connected clients.
+
+ // failure is an option ... messages are in dmesg (required by standard)
+ LogAudit *al = new LogAudit(logBuf, reader, fdDmesg);
+ if (al->startListener()) {
+ delete al;
+ close(fdDmesg);
+ }
+
pause();
exit(0);
}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index a07790a..dfb98f0 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -177,6 +177,9 @@
chown system log /proc/last_kmsg
chmod 0440 /proc/last_kmsg
+ # make the selinux kernel policy world-readable
+ chmod 0444 /sys/fs/selinux/policy
+
# create the lost+found directories, so as to enforce our permissions
mkdir /cache/lost+found 0770 root root
diff --git a/toolbox/date.c b/toolbox/date.c
index d6c9052..aa3b72e 100644
--- a/toolbox/date.c
+++ b/toolbox/date.c
@@ -1,10 +1,13 @@
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
#include <string.h>
-#include <errno.h>
#include <time.h>
+#include <unistd.h>
+
#include <linux/android_alarm.h>
#include <linux/rtc.h>
#include <sys/ioctl.h>
@@ -108,6 +111,33 @@
settime_rtc_tm(&tm);
}
+static char *parse_time(const char *str, struct timeval *ts) {
+ char *s;
+ long fs = 0; /* fractional seconds */
+
+ ts->tv_sec = strtoumax(str, &s, 10);
+
+ if (*s == '.') {
+ s++;
+ int count = 0;
+
+ /* read up to 6 digits (microseconds) */
+ while (*s && isdigit(*s)) {
+ if (++count < 7) {
+ fs = fs*10 + (*s - '0');
+ }
+ s++;
+ }
+
+ for (; count < 6; count++) {
+ fs *= 10;
+ }
+ }
+
+ ts->tv_usec = fs;
+ return s;
+}
+
int date_main(int argc, char *argv[])
{
int c;
@@ -181,7 +211,7 @@
//strptime(argv[optind], NULL, &tm);
//tv.tv_sec = mktime(&tm);
//tv.tv_usec = 0;
- strtotimeval(argv[optind], &tv);
+ parse_time(argv[optind], &tv);
printf("time %s -> %lu.%lu\n", argv[optind], tv.tv_sec, tv.tv_usec);
res = settime_alarm_timeval(&tv);
if (res < 0)