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 dabf4d56a9a46e4481c9a506f5ddf9d5490a011d)

Author: Nick Kralevich <nnk@google.com>
Change-Id: Ice88b1412d8f9daa7f9119b2b5aaf684a5e28098
diff --git a/logd/Android.mk b/logd/Android.mk
new file mode 100644
index 0000000..f536dad
--- /dev/null
+++ b/logd/Android.mk
@@ -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)
+
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
new file mode 100644
index 0000000..f5cb8dc
--- /dev/null
+++ b/logd/CommandListener.cpp
@@ -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;
+}
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
new file mode 100644
index 0000000..861abbf
--- /dev/null
+++ b/logd/CommandListener.h
@@ -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
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
new file mode 100644
index 0000000..b848fd0
--- /dev/null
+++ b/logd/FlushCommand.cpp
@@ -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 &times = 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);
+}
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
new file mode 100644
index 0000000..715daac
--- /dev/null
+++ b/logd/FlushCommand.h
@@ -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
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
new file mode 100644
index 0000000..8b273e2
--- /dev/null
+++ b/logd/LogBuffer.cpp
@@ -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;
+}
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
new file mode 100644
index 0000000..7c69f1b
--- /dev/null
+++ b/logd/LogBuffer.h
@@ -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
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
new file mode 100644
index 0000000..1c55623
--- /dev/null
+++ b/logd/LogBufferElement.cpp
@@ -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;
+}
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
new file mode 100644
index 0000000..390c97c
--- /dev/null
+++ b/logd/LogBufferElement.h
@@ -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
diff --git a/logd/LogCommand.cpp b/logd/LogCommand.cpp
new file mode 100644
index 0000000..ec8365a
--- /dev/null
+++ b/logd/LogCommand.cpp
@@ -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)  {
+}
diff --git a/logd/LogCommand.h b/logd/LogCommand.h
new file mode 100644
index 0000000..aef6706
--- /dev/null
+++ b/logd/LogCommand.h
@@ -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
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
new file mode 100644
index 0000000..c6b248b
--- /dev/null
+++ b/logd/LogListener.cpp
@@ -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;
+}
diff --git a/logd/LogListener.h b/logd/LogListener.h
new file mode 100644
index 0000000..7099e13
--- /dev/null
+++ b/logd/LogListener.h
@@ -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
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
new file mode 100644
index 0000000..5b540bf
--- /dev/null
+++ b/logd/LogReader.cpp
@@ -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 &times = 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();
+}
diff --git a/logd/LogReader.h b/logd/LogReader.h
new file mode 100644
index 0000000..b267c75
--- /dev/null
+++ b/logd/LogReader.h
@@ -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
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
new file mode 100644
index 0000000..d6d4e93
--- /dev/null
+++ b/logd/LogTimes.cpp
@@ -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 &times = 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;
+}
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
new file mode 100644
index 0000000..ac52db2
--- /dev/null
+++ b/logd/LogTimes.h
@@ -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(&timesLock); }
+    static void unlock(void) { pthread_mutex_unlock(&timesLock); }
+
+    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
diff --git a/logd/main.cpp b/logd/main.cpp
new file mode 100644
index 0000000..667e5bb
--- /dev/null
+++ b/logd/main.cpp
@@ -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);
+}
+