logd: initial checkin.
* Create a new userspace log daemon for handling logging messages.
Original-Change-Id: I75267df16359684490121e6c31cca48614d79856
Signed-off-by: Nick Kralevich <nnk@google.com>
* Merge conflicts
* rename new syslog daemon to logd to prevent confusion with bionic syslog
* replace racy getGroups call with KISS call to client->getGid()
* Timestamps are filed at logging source
* insert entries into list in timestamp order
* Added LogTimeEntry tail filtration handling
* Added region locking around LogWriter list
* separate threads for each writer
* /dev/socket/logd* permissions
Signed-off-by: Mark Salyzyn <salyzyn@google.com>
(cherry picked from commit 3e76e0a497
)
Author: Nick Kralevich <nnk@google.com>
Change-Id: Ice88b1412d8f9daa7f9119b2b5aaf684a5e28098
This commit is contained in:
parent
9b986497e7
commit
0175b0747a
22 changed files with 1555 additions and 8 deletions
|
@ -23,12 +23,12 @@
|
|||
#ifdef __cplusplus
|
||||
struct log_time : public timespec {
|
||||
public:
|
||||
log_time(timespec &T)
|
||||
log_time(const timespec &T)
|
||||
{
|
||||
tv_sec = T.tv_sec;
|
||||
tv_nsec = T.tv_nsec;
|
||||
}
|
||||
log_time(void)
|
||||
log_time()
|
||||
{
|
||||
}
|
||||
log_time(clockid_t id)
|
||||
|
@ -67,7 +67,7 @@ public:
|
|||
{
|
||||
return !(*this > T);
|
||||
}
|
||||
uint64_t nsec(void) const
|
||||
uint64_t nsec() const
|
||||
{
|
||||
return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ struct logger_entry {
|
|||
/*
|
||||
* The userspace structure for version 2 of the logger_entry ABI.
|
||||
* This structure is returned to userspace if ioctl(LOGGER_SET_VERSION)
|
||||
* is called with version==2
|
||||
* is called with version==2; or used with the user space log daemon.
|
||||
*/
|
||||
struct logger_entry_v2 {
|
||||
uint16_t len; /* length of the payload */
|
||||
|
@ -48,6 +48,17 @@ struct logger_entry_v2 {
|
|||
char msg[0]; /* the entry's payload */
|
||||
};
|
||||
|
||||
struct logger_entry_v3 {
|
||||
uint16_t len; /* length of the payload */
|
||||
uint16_t hdr_size; /* sizeof(struct logger_entry_v2) */
|
||||
int32_t pid; /* generating process's pid */
|
||||
int32_t tid; /* generating process's tid */
|
||||
int32_t sec; /* seconds since Epoch */
|
||||
int32_t nsec; /* nanoseconds */
|
||||
uint32_t lid; /* log id of the payload */
|
||||
char msg[0]; /* the entry's payload */
|
||||
};
|
||||
|
||||
/*
|
||||
* The maximum size of the log entry payload that can be
|
||||
* written to the kernel logger driver. An attempt to write
|
||||
|
@ -69,6 +80,7 @@ struct log_msg {
|
|||
union {
|
||||
unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
|
||||
struct logger_entry_v2 entry;
|
||||
struct logger_entry_v3 entry_v3;
|
||||
struct logger_entry_v2 entry_v2;
|
||||
struct logger_entry entry_v1;
|
||||
struct {
|
||||
|
@ -106,21 +118,21 @@ struct log_msg {
|
|||
{
|
||||
return !(*this > T);
|
||||
}
|
||||
uint64_t nsec(void) const
|
||||
uint64_t nsec() const
|
||||
{
|
||||
return static_cast<uint64_t>(entry.sec) * NS_PER_SEC + entry.nsec;
|
||||
}
|
||||
|
||||
/* packet methods */
|
||||
log_id_t id(void)
|
||||
log_id_t id()
|
||||
{
|
||||
return extra.id;
|
||||
}
|
||||
char *msg(void)
|
||||
char *msg()
|
||||
{
|
||||
return entry.hdr_size ? (char *) buf + entry.hdr_size : entry_v1.msg;
|
||||
}
|
||||
unsigned int len(void)
|
||||
unsigned int len()
|
||||
{
|
||||
return (entry.hdr_size ? entry.hdr_size : sizeof(entry_v1)) + entry.len;
|
||||
}
|
||||
|
|
|
@ -76,6 +76,7 @@
|
|||
#define AID_SDCARD_PICS 1033 /* external storage photos access */
|
||||
#define AID_SDCARD_AV 1034 /* external storage audio/video access */
|
||||
#define AID_SDCARD_ALL 1035 /* access all users external storage */
|
||||
#define AID_LOGD 1036 /* log daemon */
|
||||
|
||||
#define AID_SHELL 2000 /* adb and debug shell user */
|
||||
#define AID_CACHE 2001 /* cache access */
|
||||
|
@ -151,6 +152,7 @@ static const struct android_id_info android_ids[] = {
|
|||
{ "sdcard_pics", AID_SDCARD_PICS, },
|
||||
{ "sdcard_av", AID_SDCARD_AV, },
|
||||
{ "sdcard_all", AID_SDCARD_ALL, },
|
||||
{ "logd", AID_LOGD, },
|
||||
|
||||
{ "shell", AID_SHELL, },
|
||||
{ "cache", AID_CACHE, },
|
||||
|
|
28
logd/Android.mk
Normal file
28
logd/Android.mk
Normal file
|
@ -0,0 +1,28 @@
|
|||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE:= logd
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
main.cpp \
|
||||
LogCommand.cpp \
|
||||
CommandListener.cpp \
|
||||
LogListener.cpp \
|
||||
LogReader.cpp \
|
||||
FlushCommand.cpp \
|
||||
LogBuffer.cpp \
|
||||
LogBufferElement.cpp \
|
||||
LogTimes.cpp
|
||||
|
||||
LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libsysutils \
|
||||
liblog \
|
||||
libcutils
|
||||
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
137
logd/CommandListener.cpp
Normal file
137
logd/CommandListener.cpp
Normal file
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <sysutils/SocketClient.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
||||
#include "CommandListener.h"
|
||||
|
||||
CommandListener::CommandListener(LogBuffer *buf, LogReader * /*reader*/,
|
||||
LogListener * /*swl*/)
|
||||
: FrameworkListener("logd")
|
||||
, mBuf(*buf) {
|
||||
// registerCmd(new ShutdownCmd(buf, writer, swl));
|
||||
registerCmd(new ClearCmd(buf));
|
||||
registerCmd(new GetBufSizeCmd(buf));
|
||||
registerCmd(new GetBufSizeUsedCmd(buf));
|
||||
}
|
||||
|
||||
CommandListener::ShutdownCmd::ShutdownCmd(LogBuffer *buf, LogReader *reader,
|
||||
LogListener *swl)
|
||||
: LogCommand("shutdown")
|
||||
, mBuf(*buf)
|
||||
, mReader(*reader)
|
||||
, mSwl(*swl)
|
||||
{ }
|
||||
|
||||
int CommandListener::ShutdownCmd::runCommand(SocketClient * /*cli*/,
|
||||
int /*argc*/,
|
||||
char ** /*argv*/) {
|
||||
mSwl.stopListener();
|
||||
mReader.stopListener();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
CommandListener::ClearCmd::ClearCmd(LogBuffer *buf)
|
||||
: LogCommand("clear")
|
||||
, mBuf(*buf)
|
||||
{ }
|
||||
|
||||
int CommandListener::ClearCmd::runCommand(SocketClient *cli,
|
||||
int argc, char **argv) {
|
||||
if ((cli->getUid() != AID_ROOT)
|
||||
&& (cli->getGid() != AID_ROOT)
|
||||
&& (cli->getGid() != AID_LOG)) {
|
||||
cli->sendMsg("Permission Denied");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
cli->sendMsg("Missing Argument");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int id = atoi(argv[1]);
|
||||
if ((id < LOG_ID_MIN) || (id >= LOG_ID_MAX)) {
|
||||
cli->sendMsg("Range Error");
|
||||
return 0;
|
||||
}
|
||||
|
||||
mBuf.clear((log_id_t) id);
|
||||
cli->sendMsg("success");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
CommandListener::GetBufSizeCmd::GetBufSizeCmd(LogBuffer *buf)
|
||||
: LogCommand("getLogSize")
|
||||
, mBuf(*buf)
|
||||
{ }
|
||||
|
||||
int CommandListener::GetBufSizeCmd::runCommand(SocketClient *cli,
|
||||
int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
cli->sendMsg("Missing Argument");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int id = atoi(argv[1]);
|
||||
if ((id < LOG_ID_MIN) || (id >= LOG_ID_MAX)) {
|
||||
cli->sendMsg("Range Error");
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long size = mBuf.getSize((log_id_t) id);
|
||||
char buf[512];
|
||||
snprintf(buf, sizeof(buf), "%lu", size);
|
||||
cli->sendMsg(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer *buf)
|
||||
: LogCommand("getLogSizeUsed")
|
||||
, mBuf(*buf)
|
||||
{ }
|
||||
|
||||
int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient *cli,
|
||||
int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
cli->sendMsg("Missing Argument");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int id = atoi(argv[1]);
|
||||
if ((id < LOG_ID_MIN) || (id >= LOG_ID_MAX)) {
|
||||
cli->sendMsg("Range Error");
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long size = mBuf.getSizeUsed((log_id_t) id);
|
||||
char buf[512];
|
||||
snprintf(buf, sizeof(buf), "%lu", size);
|
||||
cli->sendMsg(buf);
|
||||
return 0;
|
||||
}
|
59
logd/CommandListener.h
Normal file
59
logd/CommandListener.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (C) 2012-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 _COMMANDLISTENER_H__
|
||||
#define _COMMANDLISTENER_H__
|
||||
|
||||
#include <sysutils/FrameworkListener.h>
|
||||
#include "LogCommand.h"
|
||||
#include "LogBuffer.h"
|
||||
#include "LogReader.h"
|
||||
#include "LogListener.h"
|
||||
|
||||
class CommandListener : public FrameworkListener {
|
||||
LogBuffer &mBuf;
|
||||
|
||||
public:
|
||||
CommandListener(LogBuffer *buf, LogReader *reader, LogListener *swl);
|
||||
virtual ~CommandListener() {}
|
||||
|
||||
private:
|
||||
class ShutdownCmd : public LogCommand {
|
||||
LogBuffer &mBuf;
|
||||
LogReader &mReader;
|
||||
LogListener &mSwl;
|
||||
|
||||
public:
|
||||
ShutdownCmd(LogBuffer *buf, LogReader *reader, LogListener *swl);
|
||||
virtual ~ShutdownCmd() {}
|
||||
int runCommand(SocketClient *c, int argc, char ** argv);
|
||||
};
|
||||
|
||||
#define LogBufferCmd(name) \
|
||||
class name##Cmd : public LogCommand { \
|
||||
LogBuffer &mBuf; \
|
||||
public: \
|
||||
name##Cmd(LogBuffer *buf); \
|
||||
virtual ~name##Cmd() {} \
|
||||
int runCommand(SocketClient *c, int argc, char ** argv); \
|
||||
};
|
||||
|
||||
LogBufferCmd(Clear)
|
||||
LogBufferCmd(GetBufSize)
|
||||
LogBufferCmd(GetBufSizeUsed)
|
||||
};
|
||||
|
||||
#endif
|
86
logd/FlushCommand.cpp
Normal file
86
logd/FlushCommand.cpp
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
#include "FlushCommand.h"
|
||||
#include "LogBufferElement.h"
|
||||
#include "LogTimes.h"
|
||||
#include "LogReader.h"
|
||||
|
||||
FlushCommand::FlushCommand(LogReader &reader,
|
||||
bool nonBlock,
|
||||
unsigned long tail,
|
||||
unsigned int logMask,
|
||||
pid_t pid)
|
||||
: mReader(reader)
|
||||
, mNonBlock(nonBlock)
|
||||
, mTail(tail)
|
||||
, mLogMask(logMask)
|
||||
, mPid(pid)
|
||||
{ }
|
||||
|
||||
// runSocketCommand is called once for every open client on the
|
||||
// log reader socket. Here we manage and associated the reader
|
||||
// client tracking and log region locks LastLogTimes list of
|
||||
// LogTimeEntrys, and spawn a transitory per-client thread to
|
||||
// work at filing data to the socket.
|
||||
//
|
||||
// global LogTimeEntry::lock() is used to protect access,
|
||||
// reference counts are used to ensure that individual
|
||||
// LogTimeEntry lifetime is managed when not protected.
|
||||
void FlushCommand::runSocketCommand(SocketClient *client) {
|
||||
LogTimeEntry *entry = NULL;
|
||||
LastLogTimes × = mReader.logbuf().mTimes;
|
||||
|
||||
LogTimeEntry::lock();
|
||||
LastLogTimes::iterator it = times.begin();
|
||||
while(it != times.end()) {
|
||||
entry = (*it);
|
||||
if (entry->mClient == client) {
|
||||
entry->triggerReader_Locked();
|
||||
if (entry->runningReader_Locked()) {
|
||||
LogTimeEntry::unlock();
|
||||
return;
|
||||
}
|
||||
entry->incRef_Locked();
|
||||
break;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
if (it == times.end()) {
|
||||
/* Create LogTimeEntry in notifyNewLog() ? */
|
||||
if (mTail == (unsigned long) -1) {
|
||||
LogTimeEntry::unlock();
|
||||
return;
|
||||
}
|
||||
entry = new LogTimeEntry(mReader, client, mNonBlock, mTail, mLogMask, mPid);
|
||||
times.push_back(entry);
|
||||
}
|
||||
|
||||
client->incRef();
|
||||
|
||||
/* release client and entry reference counts once done */
|
||||
entry->startReader_Locked();
|
||||
LogTimeEntry::unlock();
|
||||
}
|
||||
|
||||
bool FlushCommand::hasReadLogs(SocketClient *client) {
|
||||
return (client->getUid() == AID_ROOT)
|
||||
|| (client->getGid() == AID_ROOT)
|
||||
|| (client->getGid() == AID_LOG);
|
||||
}
|
41
logd/FlushCommand.h
Normal file
41
logd/FlushCommand.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef _FLUSH_COMMAND_H
|
||||
#define _FLUSH_COMMAND_H
|
||||
|
||||
#include <sysutils/SocketClientCommand.h>
|
||||
|
||||
class LogReader;
|
||||
|
||||
class FlushCommand : public SocketClientCommand {
|
||||
LogReader &mReader;
|
||||
bool mNonBlock;
|
||||
unsigned long mTail;
|
||||
unsigned int mLogMask;
|
||||
pid_t mPid;
|
||||
|
||||
public:
|
||||
FlushCommand(LogReader &mReader,
|
||||
bool nonBlock = false,
|
||||
unsigned long tail = -1,
|
||||
unsigned int logMask = -1,
|
||||
pid_t pid = 0);
|
||||
virtual void runSocketCommand(SocketClient *client);
|
||||
|
||||
static bool hasReadLogs(SocketClient *client);
|
||||
};
|
||||
|
||||
#endif
|
214
logd/LogBuffer.cpp
Normal file
214
logd/LogBuffer.cpp
Normal file
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* Copyright (C) 2012-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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <log/logger.h>
|
||||
|
||||
#include "LogBuffer.h"
|
||||
#include "LogReader.h"
|
||||
|
||||
#define LOG_BUFFER_SIZE (256 * 1024) // Tuned on a per-platform basis here?
|
||||
|
||||
LogBuffer::LogBuffer(LastLogTimes *times)
|
||||
: mTimes(*times) {
|
||||
int i;
|
||||
for (i = 0; i < LOG_ID_MAX; i++) {
|
||||
mSizes[i] = 0;
|
||||
mElements[i] = 0;
|
||||
}
|
||||
pthread_mutex_init(&mLogElementsLock, NULL);
|
||||
}
|
||||
|
||||
void LogBuffer::log(log_id_t log_id, struct timespec realtime,
|
||||
uid_t uid, pid_t pid, const char *msg,
|
||||
unsigned short len) {
|
||||
if ((log_id >= LOG_ID_MAX) || (log_id < 0)) {
|
||||
return;
|
||||
}
|
||||
LogBufferElement *elem = new LogBufferElement(log_id, realtime,
|
||||
uid, pid, msg, len);
|
||||
|
||||
pthread_mutex_lock(&mLogElementsLock);
|
||||
|
||||
// Insert elements in time sorted order if possible
|
||||
// NB: if end is region locked, place element at end of list
|
||||
LogBufferElementCollection::iterator it = mLogElements.end();
|
||||
LogBufferElementCollection::iterator last = it;
|
||||
while (--it != mLogElements.begin()) {
|
||||
if ((*it)->getRealTime() <= elem->getRealTime()) {
|
||||
break;
|
||||
}
|
||||
last = it;
|
||||
}
|
||||
if (last == mLogElements.end()) {
|
||||
mLogElements.push_back(elem);
|
||||
} else {
|
||||
log_time end;
|
||||
bool end_set = false;
|
||||
bool end_always = false;
|
||||
|
||||
LogTimeEntry::lock();
|
||||
|
||||
LastLogTimes::iterator t = mTimes.begin();
|
||||
while(t != mTimes.end()) {
|
||||
LogTimeEntry *entry = (*t);
|
||||
if (entry->owned_Locked()) {
|
||||
if (!entry->mNonBlock) {
|
||||
end_always = true;
|
||||
break;
|
||||
}
|
||||
if (!end_set || (end <= entry->mEnd)) {
|
||||
end = entry->mEnd;
|
||||
end_set = true;
|
||||
}
|
||||
}
|
||||
t++;
|
||||
}
|
||||
|
||||
if (end_always
|
||||
|| (end_set && (end >= (*last)->getMonotonicTime()))) {
|
||||
mLogElements.push_back(elem);
|
||||
} else {
|
||||
mLogElements.insert(last,elem);
|
||||
}
|
||||
|
||||
LogTimeEntry::unlock();
|
||||
}
|
||||
|
||||
mSizes[log_id] += len;
|
||||
mElements[log_id]++;
|
||||
maybePrune(log_id);
|
||||
pthread_mutex_unlock(&mLogElementsLock);
|
||||
}
|
||||
|
||||
// If we're using more than 256K of memory for log entries, prune
|
||||
// 10% of the log entries.
|
||||
//
|
||||
// mLogElementsLock must be held when this function is called.
|
||||
void LogBuffer::maybePrune(log_id_t id) {
|
||||
if (mSizes[id] > LOG_BUFFER_SIZE) {
|
||||
prune(id, mElements[id] / 10);
|
||||
}
|
||||
}
|
||||
|
||||
// prune "pruneRows" of type "id" from the buffer.
|
||||
//
|
||||
// mLogElementsLock must be held when this function is called.
|
||||
void LogBuffer::prune(log_id_t id, unsigned long pruneRows) {
|
||||
LogTimeEntry *oldest = NULL;
|
||||
|
||||
LogTimeEntry::lock();
|
||||
|
||||
// Region locked?
|
||||
LastLogTimes::iterator t = mTimes.begin();
|
||||
while(t != mTimes.end()) {
|
||||
LogTimeEntry *entry = (*t);
|
||||
if (entry->owned_Locked()
|
||||
&& (!oldest || (oldest->mStart > entry->mStart))) {
|
||||
oldest = entry;
|
||||
}
|
||||
t++;
|
||||
}
|
||||
|
||||
LogBufferElementCollection::iterator it = mLogElements.begin();
|
||||
while((pruneRows > 0) && (it != mLogElements.end())) {
|
||||
LogBufferElement *e = *it;
|
||||
if (e->getLogId() == id) {
|
||||
if (oldest && (oldest->mStart <= e->getMonotonicTime())) {
|
||||
if (mSizes[id] > (2 * LOG_BUFFER_SIZE)) {
|
||||
// kick a misbehaving log reader client off the island
|
||||
oldest->release_Locked();
|
||||
} else {
|
||||
oldest->triggerSkip_Locked(pruneRows);
|
||||
}
|
||||
break;
|
||||
}
|
||||
it = mLogElements.erase(it);
|
||||
mSizes[id] -= e->getMsgLen();
|
||||
mElements[id]--;
|
||||
delete e;
|
||||
pruneRows--;
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
LogTimeEntry::unlock();
|
||||
}
|
||||
|
||||
// clear all rows of type "id" from the buffer.
|
||||
void LogBuffer::clear(log_id_t id) {
|
||||
pthread_mutex_lock(&mLogElementsLock);
|
||||
prune(id, ULONG_MAX);
|
||||
pthread_mutex_unlock(&mLogElementsLock);
|
||||
}
|
||||
|
||||
// get the used space associated with "id".
|
||||
unsigned long LogBuffer::getSizeUsed(log_id_t id) {
|
||||
pthread_mutex_lock(&mLogElementsLock);
|
||||
unsigned long retval = mSizes[id];
|
||||
pthread_mutex_unlock(&mLogElementsLock);
|
||||
return retval;
|
||||
}
|
||||
|
||||
// get the total space allocated to "id"
|
||||
unsigned long LogBuffer::getSize(log_id_t /*id*/) {
|
||||
return LOG_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
struct timespec LogBuffer::flushTo(
|
||||
SocketClient *reader, const struct timespec start, bool privileged,
|
||||
bool (*filter)(const LogBufferElement *element, void *arg), void *arg) {
|
||||
LogBufferElementCollection::iterator it;
|
||||
log_time max = start;
|
||||
uid_t uid = reader->getUid();
|
||||
|
||||
pthread_mutex_lock(&mLogElementsLock);
|
||||
for (it = mLogElements.begin(); it != mLogElements.end(); ++it) {
|
||||
LogBufferElement *element = *it;
|
||||
|
||||
if (!privileged && (element->getUid() != uid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (element->getMonotonicTime() <= start) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// NB: calling out to another object with mLogElementsLock held (safe)
|
||||
if (filter && !(*filter)(element, arg)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&mLogElementsLock);
|
||||
|
||||
// range locking in LastLogTimes looks after us
|
||||
max = element->flushTo(reader);
|
||||
|
||||
if (max == element->FLUSH_ERROR) {
|
||||
return max;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&mLogElementsLock);
|
||||
}
|
||||
pthread_mutex_unlock(&mLogElementsLock);
|
||||
|
||||
return max;
|
||||
}
|
60
logd/LogBuffer.h
Normal file
60
logd/LogBuffer.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (C) 2012-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_BUFFER_H__
|
||||
#define _LOGD_LOG_BUFFER_H__
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <log/log.h>
|
||||
#include <sysutils/SocketClient.h>
|
||||
#include <utils/List.h>
|
||||
|
||||
#include "LogBufferElement.h"
|
||||
#include "LogTimes.h"
|
||||
|
||||
typedef android::List<LogBufferElement *> LogBufferElementCollection;
|
||||
|
||||
class LogBuffer {
|
||||
LogBufferElementCollection mLogElements;
|
||||
pthread_mutex_t mLogElementsLock;
|
||||
|
||||
unsigned long mSizes[LOG_ID_MAX];
|
||||
unsigned long mElements[LOG_ID_MAX];
|
||||
|
||||
public:
|
||||
LastLogTimes &mTimes;
|
||||
|
||||
LogBuffer(LastLogTimes *times);
|
||||
|
||||
void log(log_id_t log_id, struct timespec realtime,
|
||||
uid_t uid, pid_t pid, const char *msg, unsigned short len);
|
||||
struct timespec flushTo(SocketClient *writer, const struct timespec start,
|
||||
bool privileged,
|
||||
bool (*filter)(const LogBufferElement *element, void *arg) = NULL,
|
||||
void *arg = NULL);
|
||||
|
||||
void clear(log_id_t id);
|
||||
unsigned long getSize(log_id_t id);
|
||||
unsigned long getSizeUsed(log_id_t id);
|
||||
|
||||
private:
|
||||
void maybePrune(log_id_t id);
|
||||
void prune(log_id_t id, unsigned long pruneRows);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
64
logd/LogBufferElement.cpp
Normal file
64
logd/LogBufferElement.cpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (C) 2012-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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <log/logger.h>
|
||||
|
||||
#include "LogBufferElement.h"
|
||||
#include "LogReader.h"
|
||||
|
||||
const struct timespec LogBufferElement::FLUSH_ERROR = { 0, 0 };
|
||||
|
||||
LogBufferElement::LogBufferElement(log_id_t log_id, struct timespec realtime, uid_t uid, pid_t pid, const char *msg, unsigned short len)
|
||||
: mLogId(log_id)
|
||||
, mUid(uid)
|
||||
, mPid(pid)
|
||||
, mMsgLen(len)
|
||||
, mMonotonicTime(CLOCK_MONOTONIC)
|
||||
, mRealTime(realtime) {
|
||||
mMsg = new char[len];
|
||||
memcpy(mMsg, msg, len);
|
||||
}
|
||||
|
||||
LogBufferElement::~LogBufferElement() {
|
||||
delete [] mMsg;
|
||||
}
|
||||
|
||||
struct timespec LogBufferElement::flushTo(SocketClient *reader) {
|
||||
struct logger_entry_v3 entry;
|
||||
memset(&entry, 0, sizeof(struct logger_entry_v3));
|
||||
entry.hdr_size = sizeof(struct logger_entry_v3);
|
||||
entry.len = mMsgLen;
|
||||
entry.lid = mLogId;
|
||||
entry.pid = mPid;
|
||||
entry.sec = mRealTime.tv_sec;
|
||||
entry.nsec = mRealTime.tv_nsec;
|
||||
|
||||
struct iovec iovec[2];
|
||||
iovec[0].iov_base = &entry;
|
||||
iovec[0].iov_len = sizeof(struct logger_entry_v3);
|
||||
iovec[1].iov_base = mMsg;
|
||||
iovec[1].iov_len = mMsgLen;
|
||||
if (reader->sendDatav(iovec, 2)) {
|
||||
return FLUSH_ERROR;
|
||||
}
|
||||
|
||||
return mMonotonicTime;
|
||||
}
|
50
logd/LogBufferElement.h
Normal file
50
logd/LogBufferElement.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (C) 2012-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_BUFFER_ELEMENT_H__
|
||||
#define _LOGD_LOG_BUFFER_ELEMENT_H__
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sysutils/SocketClient.h>
|
||||
#include <log/log.h>
|
||||
#include <log/log_read.h>
|
||||
|
||||
class LogBufferElement {
|
||||
const log_id_t mLogId;
|
||||
const uid_t mUid;
|
||||
const pid_t mPid;
|
||||
char *mMsg;
|
||||
const unsigned short mMsgLen;
|
||||
const log_time mMonotonicTime;
|
||||
const log_time mRealTime;
|
||||
|
||||
public:
|
||||
LogBufferElement(log_id_t log_id, struct timespec realtime,
|
||||
uid_t uid, pid_t pid, const char *msg, unsigned short len);
|
||||
virtual ~LogBufferElement();
|
||||
|
||||
log_id_t getLogId() const { return mLogId; }
|
||||
uid_t getUid(void) const { return mUid; }
|
||||
pid_t getPid(void) const { return mPid; }
|
||||
unsigned short getMsgLen() const { return mMsgLen; }
|
||||
log_time getMonotonicTime(void) const { return mMonotonicTime; }
|
||||
log_time getRealTime(void) const { return mRealTime; }
|
||||
|
||||
static const struct timespec FLUSH_ERROR;
|
||||
struct timespec flushTo(SocketClient *writer);
|
||||
};
|
||||
|
||||
#endif
|
21
logd/LogCommand.cpp
Normal file
21
logd/LogCommand.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "LogCommand.h"
|
||||
|
||||
LogCommand::LogCommand(const char *cmd) :
|
||||
FrameworkCommand(cmd) {
|
||||
}
|
28
logd/LogCommand.h
Normal file
28
logd/LogCommand.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LOGD_COMMAND_H
|
||||
#define _LOGD_COMMAND_H
|
||||
|
||||
#include <sysutils/FrameworkCommand.h>
|
||||
|
||||
class LogCommand : public FrameworkCommand {
|
||||
public:
|
||||
LogCommand(const char *cmd);
|
||||
virtual ~LogCommand() {}
|
||||
};
|
||||
|
||||
#endif
|
108
logd/LogListener.cpp
Normal file
108
logd/LogListener.cpp
Normal file
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright (C) 2012-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 <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cutils/sockets.h>
|
||||
#include <log/logger.h>
|
||||
|
||||
#include "LogListener.h"
|
||||
|
||||
LogListener::LogListener(LogBuffer *buf, LogReader *reader)
|
||||
: SocketListener(getLogSocket(), false)
|
||||
, logbuf(buf)
|
||||
, reader(reader)
|
||||
{ }
|
||||
|
||||
bool LogListener::onDataAvailable(SocketClient *cli) {
|
||||
char buffer[1024];
|
||||
struct iovec iov = { buffer, sizeof(buffer) };
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
char control[CMSG_SPACE(sizeof(struct ucred))];
|
||||
struct msghdr hdr = {
|
||||
NULL,
|
||||
0,
|
||||
&iov,
|
||||
1,
|
||||
control,
|
||||
sizeof(control),
|
||||
0,
|
||||
};
|
||||
|
||||
int socket = cli->getSocket();
|
||||
|
||||
ssize_t n = recvmsg(socket, &hdr, 0);
|
||||
if (n <= (ssize_t) sizeof_log_id_t) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct ucred *cred = NULL;
|
||||
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);
|
||||
while (cmsg != NULL) {
|
||||
if (cmsg->cmsg_level == SOL_SOCKET
|
||||
&& cmsg->cmsg_type == SCM_CREDENTIALS) {
|
||||
cred = (struct ucred *)CMSG_DATA(cmsg);
|
||||
break;
|
||||
}
|
||||
cmsg = CMSG_NXTHDR(&hdr, cmsg);
|
||||
}
|
||||
|
||||
if (cred == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cred->uid == getuid()) {
|
||||
// ignore log messages we send to ourself.
|
||||
// Such log messages are often generated by libraries we depend on
|
||||
// which use standard Android logging.
|
||||
return false;
|
||||
}
|
||||
|
||||
// First log element is always log_id.
|
||||
log_id_t log_id = (log_id_t) *((typeof_log_id_t *) buffer);
|
||||
if (log_id < 0 || log_id >= LOG_ID_MAX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char *msg = ((char *)buffer) + sizeof_log_id_t;
|
||||
n -= sizeof_log_id_t;
|
||||
|
||||
log_time realtime(msg);
|
||||
msg += sizeof(log_time);
|
||||
n -= sizeof(log_time);
|
||||
|
||||
unsigned short len = n;
|
||||
if (len == n) {
|
||||
logbuf->log(log_id, realtime, cred->uid, cred->pid, msg, len);
|
||||
reader->notifyNewLog();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int LogListener::getLogSocket() {
|
||||
int sock = android_get_control_socket("logdw");
|
||||
int on = 1;
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
return sock;
|
||||
}
|
37
logd/LogListener.h
Normal file
37
logd/LogListener.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LOGD_LOG_LISTENER_H__
|
||||
#define _LOGD_LOG_LISTENER_H__
|
||||
|
||||
#include <sysutils/SocketListener.h>
|
||||
#include "LogReader.h"
|
||||
|
||||
class LogListener : public SocketListener {
|
||||
LogBuffer *logbuf;
|
||||
LogReader *reader;
|
||||
|
||||
public:
|
||||
LogListener(LogBuffer *buf, LogReader *reader);
|
||||
|
||||
protected:
|
||||
virtual bool onDataAvailable(SocketClient *cli);
|
||||
|
||||
private:
|
||||
static int getLogSocket();
|
||||
};
|
||||
|
||||
#endif
|
105
logd/LogReader.cpp
Normal file
105
logd/LogReader.cpp
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <cutils/sockets.h>
|
||||
|
||||
#include "LogReader.h"
|
||||
#include "FlushCommand.h"
|
||||
|
||||
LogReader::LogReader(LogBuffer *logbuf)
|
||||
: SocketListener("logdr", true)
|
||||
, mLogbuf(*logbuf)
|
||||
{ }
|
||||
|
||||
// When we are notified a new log entry is available, inform
|
||||
// all of our listening sockets.
|
||||
void LogReader::notifyNewLog() {
|
||||
FlushCommand command(*this);
|
||||
runOnEachSocket(&command);
|
||||
}
|
||||
|
||||
bool LogReader::onDataAvailable(SocketClient *cli) {
|
||||
char buffer[255];
|
||||
|
||||
int len = read(cli->getSocket(), buffer, sizeof(buffer) - 1);
|
||||
if (len <= 0) {
|
||||
doSocketDelete(cli);
|
||||
return false;
|
||||
}
|
||||
buffer[len] = '\0';
|
||||
|
||||
unsigned long tail = 0;
|
||||
static const char _tail[] = " tail=";
|
||||
char *cp = strstr(buffer, _tail);
|
||||
if (cp) {
|
||||
tail = atol(cp + sizeof(_tail) - 1);
|
||||
}
|
||||
|
||||
unsigned int logMask = -1;
|
||||
static const char _logIds[] = " lids=";
|
||||
cp = strstr(buffer, _logIds);
|
||||
if (cp) {
|
||||
logMask = 0;
|
||||
cp += sizeof(_logIds) - 1;
|
||||
while (*cp && *cp != '\0') {
|
||||
int val = 0;
|
||||
while (('0' <= *cp) && (*cp <= '9')) {
|
||||
val *= 10;
|
||||
val += *cp - '0';
|
||||
++cp;
|
||||
}
|
||||
logMask |= 1 << val;
|
||||
if (*cp != ',') {
|
||||
break;
|
||||
}
|
||||
++cp;
|
||||
}
|
||||
}
|
||||
|
||||
pid_t pid = 0;
|
||||
static const char _pid[] = " pid=";
|
||||
cp = strstr(buffer, _pid);
|
||||
if (cp) {
|
||||
pid = atol(cp + sizeof(_pid) - 1);
|
||||
}
|
||||
|
||||
bool nonBlock = false;
|
||||
if (strncmp(buffer, "dumpAndClose", 12) == 0) {
|
||||
nonBlock = true;
|
||||
}
|
||||
|
||||
FlushCommand command(*this, nonBlock, tail, logMask, pid);
|
||||
command.runSocketCommand(cli);
|
||||
return true;
|
||||
}
|
||||
|
||||
void LogReader::doSocketDelete(SocketClient *cli) {
|
||||
LastLogTimes × = mLogbuf.mTimes;
|
||||
LogTimeEntry::lock();
|
||||
LastLogTimes::iterator it = times.begin();
|
||||
while(it != times.end()) {
|
||||
LogTimeEntry *entry = (*it);
|
||||
if (entry->mClient == cli) {
|
||||
times.erase(it);
|
||||
entry->release_Locked();
|
||||
break;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
LogTimeEntry::unlock();
|
||||
}
|
41
logd/LogReader.h
Normal file
41
logd/LogReader.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LOGD_LOG_WRITER_H__
|
||||
#define _LOGD_LOG_WRITER_H__
|
||||
|
||||
#include <sysutils/SocketListener.h>
|
||||
#include "LogBuffer.h"
|
||||
#include "LogTimes.h"
|
||||
|
||||
class LogReader : public SocketListener {
|
||||
LogBuffer &mLogbuf;
|
||||
|
||||
public:
|
||||
LogReader(LogBuffer *logbuf);
|
||||
void notifyNewLog();
|
||||
|
||||
LogBuffer &logbuf(void) const { return mLogbuf; }
|
||||
|
||||
protected:
|
||||
virtual bool onDataAvailable(SocketClient *cli);
|
||||
|
||||
private:
|
||||
void doSocketDelete(SocketClient *cli);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
225
logd/LogTimes.cpp
Normal file
225
logd/LogTimes.cpp
Normal file
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* 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 "FlushCommand.h"
|
||||
#include "LogBuffer.h"
|
||||
#include "LogTimes.h"
|
||||
#include "LogReader.h"
|
||||
|
||||
pthread_mutex_t LogTimeEntry::timesLock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
const struct timespec LogTimeEntry::EPOCH = { 0, 1 };
|
||||
|
||||
LogTimeEntry::LogTimeEntry(LogReader &reader, SocketClient *client,
|
||||
bool nonBlock, unsigned long tail,
|
||||
unsigned int logMask, pid_t pid)
|
||||
: mRefCount(1)
|
||||
, mRelease(false)
|
||||
, mError(false)
|
||||
, threadRunning(false)
|
||||
, threadTriggered(true)
|
||||
, mReader(reader)
|
||||
, mLogMask(logMask)
|
||||
, mPid(pid)
|
||||
, skipAhead(0)
|
||||
, mCount(0)
|
||||
, mTail(tail)
|
||||
, mIndex(0)
|
||||
, mClient(client)
|
||||
, mStart(EPOCH)
|
||||
, mNonBlock(nonBlock)
|
||||
, mEnd(CLOCK_MONOTONIC)
|
||||
{ }
|
||||
|
||||
void LogTimeEntry::startReader_Locked(void) {
|
||||
threadRunning = true;
|
||||
if (pthread_create(&mThread, NULL, LogTimeEntry::threadStart, this)) {
|
||||
threadRunning = false;
|
||||
if (mClient) {
|
||||
mClient->decRef();
|
||||
}
|
||||
decRef_Locked();
|
||||
}
|
||||
}
|
||||
|
||||
void LogTimeEntry::threadStop(void *obj) {
|
||||
LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
|
||||
|
||||
lock();
|
||||
|
||||
me->threadRunning = false;
|
||||
if (me->mNonBlock) {
|
||||
me->error_Locked();
|
||||
}
|
||||
|
||||
SocketClient *client = me->mClient;
|
||||
|
||||
if (me->isError_Locked()) {
|
||||
LogReader &reader = me->mReader;
|
||||
LastLogTimes × = reader.logbuf().mTimes;
|
||||
|
||||
LastLogTimes::iterator it = times.begin();
|
||||
while(it != times.end()) {
|
||||
if (*it == me) {
|
||||
times.erase(it);
|
||||
me->release_Locked();
|
||||
break;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
me->mClient = NULL;
|
||||
reader.release(client);
|
||||
}
|
||||
|
||||
if (client) {
|
||||
client->decRef();
|
||||
}
|
||||
|
||||
me->decRef_Locked();
|
||||
|
||||
unlock();
|
||||
}
|
||||
|
||||
void *LogTimeEntry::threadStart(void *obj) {
|
||||
LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
|
||||
|
||||
pthread_cleanup_push(threadStop, obj);
|
||||
|
||||
SocketClient *client = me->mClient;
|
||||
if (!client) {
|
||||
me->error();
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
LogBuffer &logbuf = me->mReader.logbuf();
|
||||
|
||||
bool privileged = FlushCommand::hasReadLogs(client);
|
||||
|
||||
lock();
|
||||
|
||||
me->threadTriggered = true;
|
||||
|
||||
while(me->threadTriggered && !me->isError_Locked()) {
|
||||
|
||||
me->threadTriggered = false;
|
||||
|
||||
log_time start = me->mStart;
|
||||
|
||||
unlock();
|
||||
|
||||
if (me->mTail) {
|
||||
logbuf.flushTo(client, start, privileged, FilterFirstPass, me);
|
||||
}
|
||||
start = logbuf.flushTo(client, start, privileged, FilterSecondPass, me);
|
||||
|
||||
if (start == LogBufferElement::FLUSH_ERROR) {
|
||||
me->error();
|
||||
}
|
||||
|
||||
if (me->mNonBlock) {
|
||||
lock();
|
||||
break;
|
||||
}
|
||||
|
||||
sched_yield();
|
||||
|
||||
lock();
|
||||
}
|
||||
|
||||
unlock();
|
||||
|
||||
pthread_exit(NULL);
|
||||
|
||||
pthread_cleanup_pop(true);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// A first pass to count the number of elements
|
||||
bool LogTimeEntry::FilterFirstPass(const LogBufferElement *element, void *obj) {
|
||||
LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
|
||||
|
||||
LogTimeEntry::lock();
|
||||
|
||||
if (me->mCount == 0) {
|
||||
me->mStart = element->getMonotonicTime();
|
||||
}
|
||||
|
||||
if ((!me->mPid || (me->mPid == element->getPid()))
|
||||
&& (me->mLogMask & (1 << element->getLogId()))) {
|
||||
++me->mCount;
|
||||
}
|
||||
|
||||
LogTimeEntry::unlock();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// A second pass to send the selected elements
|
||||
bool LogTimeEntry::FilterSecondPass(const LogBufferElement *element, void *obj) {
|
||||
LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
|
||||
|
||||
LogTimeEntry::lock();
|
||||
|
||||
if (me->skipAhead) {
|
||||
me->skipAhead--;
|
||||
}
|
||||
|
||||
me->mStart = element->getMonotonicTime();
|
||||
|
||||
// Truncate to close race between first and second pass
|
||||
if (me->mNonBlock && me->mTail && (me->mIndex >= me->mCount)) {
|
||||
goto skip;
|
||||
}
|
||||
|
||||
if ((me->mLogMask & (1 << element->getLogId())) == 0) {
|
||||
goto skip;
|
||||
}
|
||||
|
||||
if (me->mPid && (me->mPid != element->getPid())) {
|
||||
goto skip;
|
||||
}
|
||||
|
||||
if (me->isError_Locked()) {
|
||||
goto skip;
|
||||
}
|
||||
|
||||
if (!me->mTail) {
|
||||
goto ok;
|
||||
}
|
||||
|
||||
++me->mIndex;
|
||||
|
||||
if ((me->mCount > me->mTail) && (me->mIndex <= (me->mCount - me->mTail))) {
|
||||
goto skip;
|
||||
}
|
||||
|
||||
if (!me->mNonBlock) {
|
||||
me->mTail = 0;
|
||||
}
|
||||
|
||||
ok:
|
||||
if (!me->skipAhead) {
|
||||
LogTimeEntry::unlock();
|
||||
return true;
|
||||
}
|
||||
// FALLTHRU
|
||||
|
||||
skip:
|
||||
LogTimeEntry::unlock();
|
||||
return false;
|
||||
}
|
108
logd/LogTimes.h
Normal file
108
logd/LogTimes.h
Normal file
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LOGD_LOG_TIMES_H__
|
||||
#define _LOGD_LOG_TIMES_H__
|
||||
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sysutils/SocketClient.h>
|
||||
#include <utils/List.h>
|
||||
|
||||
class LogReader;
|
||||
|
||||
class LogTimeEntry {
|
||||
static pthread_mutex_t timesLock;
|
||||
unsigned int mRefCount;
|
||||
bool mRelease;
|
||||
bool mError;
|
||||
bool threadRunning;
|
||||
bool threadTriggered;
|
||||
pthread_t mThread;
|
||||
LogReader &mReader;
|
||||
static void *threadStart(void *me);
|
||||
static void threadStop(void *me);
|
||||
const unsigned int mLogMask;
|
||||
const pid_t mPid;
|
||||
unsigned int skipAhead;
|
||||
unsigned long mCount;
|
||||
unsigned long mTail;
|
||||
unsigned long mIndex;
|
||||
|
||||
public:
|
||||
LogTimeEntry(LogReader &reader, SocketClient *client, bool nonBlock,
|
||||
unsigned long tail, unsigned int logMask, pid_t pid);
|
||||
|
||||
SocketClient *mClient;
|
||||
static const struct timespec EPOCH;
|
||||
log_time mStart;
|
||||
const bool mNonBlock;
|
||||
const log_time mEnd; // only relevant if mNonBlock
|
||||
|
||||
// Protect List manipulations
|
||||
static void lock(void) { pthread_mutex_lock(×Lock); }
|
||||
static void unlock(void) { pthread_mutex_unlock(×Lock); }
|
||||
|
||||
void startReader_Locked(void);
|
||||
|
||||
bool runningReader_Locked(void) const
|
||||
{
|
||||
return threadRunning || mRelease || mError || mNonBlock;
|
||||
}
|
||||
void triggerReader_Locked(void) { threadTriggered = true; }
|
||||
void triggerSkip_Locked(unsigned int skip) { skipAhead = skip; }
|
||||
|
||||
// Called after LogTimeEntry removed from list, lock implicitly held
|
||||
void release_Locked(void)
|
||||
{
|
||||
mRelease = true;
|
||||
if (mRefCount || threadRunning) {
|
||||
return;
|
||||
}
|
||||
// No one else is holding a reference to this
|
||||
delete this;
|
||||
}
|
||||
|
||||
// Called to mark socket in jeopardy
|
||||
void error_Locked(void) { mError = true; }
|
||||
void error(void) { lock(); mError = true; unlock(); }
|
||||
|
||||
bool isError_Locked(void) const { return mRelease || mError; }
|
||||
|
||||
// Mark Used
|
||||
// Locking implied, grabbed when protection around loop iteration
|
||||
void incRef_Locked(void) { ++mRefCount; }
|
||||
|
||||
bool owned_Locked(void) const { return mRefCount != 0; }
|
||||
|
||||
void decRef_Locked(void)
|
||||
{
|
||||
if ((mRefCount && --mRefCount) || !mRelease || threadRunning) {
|
||||
return;
|
||||
}
|
||||
// No one else is holding a reference to this
|
||||
delete this;
|
||||
}
|
||||
|
||||
// flushTo filter callbacks
|
||||
static bool FilterFirstPass(const LogBufferElement *element, void *me);
|
||||
static bool FilterSecondPass(const LogBufferElement *element, void *me);
|
||||
};
|
||||
|
||||
typedef android::List<LogTimeEntry *> LastLogTimes;
|
||||
|
||||
#endif
|
115
logd/main.cpp
Normal file
115
logd/main.cpp
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/capability.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <linux/prctl.h>
|
||||
|
||||
#include "private/android_filesystem_config.h"
|
||||
#include "CommandListener.h"
|
||||
#include "LogBuffer.h"
|
||||
#include "LogListener.h"
|
||||
|
||||
static int drop_privs() {
|
||||
if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (setgid(AID_LOGD) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (setuid(AID_LOGD) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct __user_cap_header_struct capheader;
|
||||
struct __user_cap_data_struct capdata[2];
|
||||
memset(&capheader, 0, sizeof(capheader));
|
||||
memset(&capdata, 0, sizeof(capdata));
|
||||
capheader.version = _LINUX_CAPABILITY_VERSION_3;
|
||||
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[0].inheritable = 0;
|
||||
capdata[1].inheritable = 0;
|
||||
|
||||
if (capset(&capheader, &capdata[0]) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Foreground waits for exit of the three main persistent threads that
|
||||
// are started here. The three threads are created to manage UNIX
|
||||
// domain client sockets for writing, reading and controlling the user
|
||||
// space logger. Additional transitory per-client threads are created
|
||||
// for each reader once they register.
|
||||
int main() {
|
||||
if (drop_privs() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Serves the purpose of managing the last logs times read on a
|
||||
// socket connection, and as a reader lock on a range of log
|
||||
// entries.
|
||||
|
||||
LastLogTimes *times = new LastLogTimes();
|
||||
|
||||
// LogBuffer is the object which is responsible for holding all
|
||||
// log entries.
|
||||
|
||||
LogBuffer *logBuf = new LogBuffer(times);
|
||||
|
||||
// LogReader listens on /dev/socket/logdr. When a client
|
||||
// connects, log entries in the LogBuffer are written to the client.
|
||||
|
||||
LogReader *reader = new LogReader(logBuf);
|
||||
if (reader->startListener()) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// LogListener listens on /dev/socket/logdw for client
|
||||
// initiated log messages. New log entries are added to LogBuffer
|
||||
// and LogReader is notified to send updates to connected clients.
|
||||
|
||||
LogListener *swl = new LogListener(logBuf, reader);
|
||||
if (swl->startListener()) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Command listener listens on /dev/socket/logd for incoming logd
|
||||
// administrative commands.
|
||||
|
||||
CommandListener *cl = new CommandListener(logBuf, reader, swl);
|
||||
if (cl->startListener()) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pause();
|
||||
exit(0);
|
||||
}
|
||||
|
|
@ -456,6 +456,12 @@ service adbd /sbin/adbd --root_seclabel=u:r:su:s0
|
|||
on property:ro.kernel.qemu=1
|
||||
start adbd
|
||||
|
||||
service logd /system/bin/logd
|
||||
class main
|
||||
socket logd stream 0666 logd logd
|
||||
socket logdr seqpacket 0666 logd logd
|
||||
socket logdw dgram 0222 logd logd
|
||||
|
||||
service servicemanager /system/bin/servicemanager
|
||||
class core
|
||||
user system
|
||||
|
|
Loading…
Reference in a new issue