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 3e76e0a49760c4970b7cda6153e51026af98e4f3)

Author: Nick Kralevich <nnk@google.com>
Change-Id: Ice88b1412d8f9daa7f9119b2b5aaf684a5e28098
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;
+}