summaryrefslogtreecommitdiffstats
path: root/logd
diff options
context:
space:
mode:
authorMark Salyzyn2014-02-26 11:50:16 -0600
committerMark Salyzyn2014-02-26 11:52:35 -0600
commit0175b0747a1f55329109e84c9a1322dcb95e2848 (patch)
treea48dd103279c3efc313028a09fb4570d212e624d /logd
parent9b986497e7f19a7fde9e35eb73d765f4a09dee07 (diff)
downloadplatform-system-core-0175b0747a1f55329109e84c9a1322dcb95e2848.tar.gz
platform-system-core-0175b0747a1f55329109e84c9a1322dcb95e2848.tar.xz
platform-system-core-0175b0747a1f55329109e84c9a1322dcb95e2848.zip
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
Diffstat (limited to 'logd')
-rw-r--r--logd/Android.mk28
-rw-r--r--logd/CommandListener.cpp137
-rw-r--r--logd/CommandListener.h59
-rw-r--r--logd/FlushCommand.cpp86
-rw-r--r--logd/FlushCommand.h41
-rw-r--r--logd/LogBuffer.cpp214
-rw-r--r--logd/LogBuffer.h60
-rw-r--r--logd/LogBufferElement.cpp64
-rw-r--r--logd/LogBufferElement.h50
-rw-r--r--logd/LogCommand.cpp21
-rw-r--r--logd/LogCommand.h28
-rw-r--r--logd/LogListener.cpp108
-rw-r--r--logd/LogListener.h37
-rw-r--r--logd/LogReader.cpp105
-rw-r--r--logd/LogReader.h41
-rw-r--r--logd/LogTimes.cpp225
-rw-r--r--logd/LogTimes.h108
-rw-r--r--logd/main.cpp115
18 files changed, 1527 insertions, 0 deletions
diff --git a/logd/Android.mk b/logd/Android.mk
new file mode 100644
index 000000000..f536dad18
--- /dev/null
+++ b/logd/Android.mk
@@ -0,0 +1,28 @@
1LOCAL_PATH:= $(call my-dir)
2
3include $(CLEAR_VARS)
4
5LOCAL_MODULE:= logd
6
7LOCAL_SRC_FILES := \
8 main.cpp \
9 LogCommand.cpp \
10 CommandListener.cpp \
11 LogListener.cpp \
12 LogReader.cpp \
13 FlushCommand.cpp \
14 LogBuffer.cpp \
15 LogBufferElement.cpp \
16 LogTimes.cpp
17
18LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
19
20LOCAL_SHARED_LIBRARIES := \
21 libsysutils \
22 liblog \
23 libcutils
24
25LOCAL_MODULE_TAGS := optional
26
27include $(BUILD_EXECUTABLE)
28
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
new file mode 100644
index 000000000..f5cb8dc47
--- /dev/null
+++ b/logd/CommandListener.cpp
@@ -0,0 +1,137 @@
1/*
2 * Copyright (C) 2012-2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <arpa/inet.h>
18#include <dirent.h>
19#include <errno.h>
20#include <fcntl.h>
21#include <netinet/in.h>
22#include <string.h>
23#include <stdlib.h>
24#include <sys/socket.h>
25#include <sys/types.h>
26
27#include <sysutils/SocketClient.h>
28#include <private/android_filesystem_config.h>
29
30#include "CommandListener.h"
31
32CommandListener::CommandListener(LogBuffer *buf, LogReader * /*reader*/,
33 LogListener * /*swl*/)
34 : FrameworkListener("logd")
35 , mBuf(*buf) {
36 // registerCmd(new ShutdownCmd(buf, writer, swl));
37 registerCmd(new ClearCmd(buf));
38 registerCmd(new GetBufSizeCmd(buf));
39 registerCmd(new GetBufSizeUsedCmd(buf));
40}
41
42CommandListener::ShutdownCmd::ShutdownCmd(LogBuffer *buf, LogReader *reader,
43 LogListener *swl)
44 : LogCommand("shutdown")
45 , mBuf(*buf)
46 , mReader(*reader)
47 , mSwl(*swl)
48{ }
49
50int CommandListener::ShutdownCmd::runCommand(SocketClient * /*cli*/,
51 int /*argc*/,
52 char ** /*argv*/) {
53 mSwl.stopListener();
54 mReader.stopListener();
55 exit(0);
56}
57
58CommandListener::ClearCmd::ClearCmd(LogBuffer *buf)
59 : LogCommand("clear")
60 , mBuf(*buf)
61{ }
62
63int CommandListener::ClearCmd::runCommand(SocketClient *cli,
64 int argc, char **argv) {
65 if ((cli->getUid() != AID_ROOT)
66 && (cli->getGid() != AID_ROOT)
67 && (cli->getGid() != AID_LOG)) {
68 cli->sendMsg("Permission Denied");
69 return 0;
70 }
71
72 if (argc < 2) {
73 cli->sendMsg("Missing Argument");
74 return 0;
75 }
76
77 int id = atoi(argv[1]);
78 if ((id < LOG_ID_MIN) || (id >= LOG_ID_MAX)) {
79 cli->sendMsg("Range Error");
80 return 0;
81 }
82
83 mBuf.clear((log_id_t) id);
84 cli->sendMsg("success");
85 return 0;
86}
87
88
89CommandListener::GetBufSizeCmd::GetBufSizeCmd(LogBuffer *buf)
90 : LogCommand("getLogSize")
91 , mBuf(*buf)
92{ }
93
94int CommandListener::GetBufSizeCmd::runCommand(SocketClient *cli,
95 int argc, char **argv) {
96 if (argc < 2) {
97 cli->sendMsg("Missing Argument");
98 return 0;
99 }
100
101 int id = atoi(argv[1]);
102 if ((id < LOG_ID_MIN) || (id >= LOG_ID_MAX)) {
103 cli->sendMsg("Range Error");
104 return 0;
105 }
106
107 unsigned long size = mBuf.getSize((log_id_t) id);
108 char buf[512];
109 snprintf(buf, sizeof(buf), "%lu", size);
110 cli->sendMsg(buf);
111 return 0;
112}
113
114CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer *buf)
115 : LogCommand("getLogSizeUsed")
116 , mBuf(*buf)
117{ }
118
119int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient *cli,
120 int argc, char **argv) {
121 if (argc < 2) {
122 cli->sendMsg("Missing Argument");
123 return 0;
124 }
125
126 int id = atoi(argv[1]);
127 if ((id < LOG_ID_MIN) || (id >= LOG_ID_MAX)) {
128 cli->sendMsg("Range Error");
129 return 0;
130 }
131
132 unsigned long size = mBuf.getSizeUsed((log_id_t) id);
133 char buf[512];
134 snprintf(buf, sizeof(buf), "%lu", size);
135 cli->sendMsg(buf);
136 return 0;
137}
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
new file mode 100644
index 000000000..861abbf4b
--- /dev/null
+++ b/logd/CommandListener.h
@@ -0,0 +1,59 @@
1/*
2 * Copyright (C) 2012-2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef _COMMANDLISTENER_H__
18#define _COMMANDLISTENER_H__
19
20#include <sysutils/FrameworkListener.h>
21#include "LogCommand.h"
22#include "LogBuffer.h"
23#include "LogReader.h"
24#include "LogListener.h"
25
26class CommandListener : public FrameworkListener {
27 LogBuffer &mBuf;
28
29public:
30 CommandListener(LogBuffer *buf, LogReader *reader, LogListener *swl);
31 virtual ~CommandListener() {}
32
33private:
34 class ShutdownCmd : public LogCommand {
35 LogBuffer &mBuf;
36 LogReader &mReader;
37 LogListener &mSwl;
38
39 public:
40 ShutdownCmd(LogBuffer *buf, LogReader *reader, LogListener *swl);
41 virtual ~ShutdownCmd() {}
42 int runCommand(SocketClient *c, int argc, char ** argv);
43 };
44
45#define LogBufferCmd(name) \
46 class name##Cmd : public LogCommand { \
47 LogBuffer &mBuf; \
48 public: \
49 name##Cmd(LogBuffer *buf); \
50 virtual ~name##Cmd() {} \
51 int runCommand(SocketClient *c, int argc, char ** argv); \
52 };
53
54 LogBufferCmd(Clear)
55 LogBufferCmd(GetBufSize)
56 LogBufferCmd(GetBufSizeUsed)
57};
58
59#endif
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
new file mode 100644
index 000000000..b848fd05a
--- /dev/null
+++ b/logd/FlushCommand.cpp
@@ -0,0 +1,86 @@
1/*
2 * Copyright (C) 2012-2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdlib.h>
18#include <private/android_filesystem_config.h>
19#include "FlushCommand.h"
20#include "LogBufferElement.h"
21#include "LogTimes.h"
22#include "LogReader.h"
23
24FlushCommand::FlushCommand(LogReader &reader,
25 bool nonBlock,
26 unsigned long tail,
27 unsigned int logMask,
28 pid_t pid)
29 : mReader(reader)
30 , mNonBlock(nonBlock)
31 , mTail(tail)
32 , mLogMask(logMask)
33 , mPid(pid)
34{ }
35
36// runSocketCommand is called once for every open client on the
37// log reader socket. Here we manage and associated the reader
38// client tracking and log region locks LastLogTimes list of
39// LogTimeEntrys, and spawn a transitory per-client thread to
40// work at filing data to the socket.
41//
42// global LogTimeEntry::lock() is used to protect access,
43// reference counts are used to ensure that individual
44// LogTimeEntry lifetime is managed when not protected.
45void FlushCommand::runSocketCommand(SocketClient *client) {
46 LogTimeEntry *entry = NULL;
47 LastLogTimes &times = mReader.logbuf().mTimes;
48
49 LogTimeEntry::lock();
50 LastLogTimes::iterator it = times.begin();
51 while(it != times.end()) {
52 entry = (*it);
53 if (entry->mClient == client) {
54 entry->triggerReader_Locked();
55 if (entry->runningReader_Locked()) {
56 LogTimeEntry::unlock();
57 return;
58 }
59 entry->incRef_Locked();
60 break;
61 }
62 it++;
63 }
64
65 if (it == times.end()) {
66 /* Create LogTimeEntry in notifyNewLog() ? */
67 if (mTail == (unsigned long) -1) {
68 LogTimeEntry::unlock();
69 return;
70 }
71 entry = new LogTimeEntry(mReader, client, mNonBlock, mTail, mLogMask, mPid);
72 times.push_back(entry);
73 }
74
75 client->incRef();
76
77 /* release client and entry reference counts once done */
78 entry->startReader_Locked();
79 LogTimeEntry::unlock();
80}
81
82bool FlushCommand::hasReadLogs(SocketClient *client) {
83 return (client->getUid() == AID_ROOT)
84 || (client->getGid() == AID_ROOT)
85 || (client->getGid() == AID_LOG);
86}
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
new file mode 100644
index 000000000..715daacc0
--- /dev/null
+++ b/logd/FlushCommand.h
@@ -0,0 +1,41 @@
1/*
2 * Copyright (C) 2012-2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#ifndef _FLUSH_COMMAND_H
17#define _FLUSH_COMMAND_H
18
19#include <sysutils/SocketClientCommand.h>
20
21class LogReader;
22
23class FlushCommand : public SocketClientCommand {
24 LogReader &mReader;
25 bool mNonBlock;
26 unsigned long mTail;
27 unsigned int mLogMask;
28 pid_t mPid;
29
30public:
31 FlushCommand(LogReader &mReader,
32 bool nonBlock = false,
33 unsigned long tail = -1,
34 unsigned int logMask = -1,
35 pid_t pid = 0);
36 virtual void runSocketCommand(SocketClient *client);
37
38 static bool hasReadLogs(SocketClient *client);
39};
40
41#endif
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
new file mode 100644
index 000000000..8b273e27c
--- /dev/null
+++ b/logd/LogBuffer.cpp
@@ -0,0 +1,214 @@
1/*
2 * Copyright (C) 2012-2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdio.h>
18#include <string.h>
19#include <time.h>
20#include <unistd.h>
21
22#include <log/logger.h>
23
24#include "LogBuffer.h"
25#include "LogReader.h"
26
27#define LOG_BUFFER_SIZE (256 * 1024) // Tuned on a per-platform basis here?
28
29LogBuffer::LogBuffer(LastLogTimes *times)
30 : mTimes(*times) {
31 int i;
32 for (i = 0; i < LOG_ID_MAX; i++) {
33 mSizes[i] = 0;
34 mElements[i] = 0;
35 }
36 pthread_mutex_init(&mLogElementsLock, NULL);
37}
38
39void LogBuffer::log(log_id_t log_id, struct timespec realtime,
40 uid_t uid, pid_t pid, const char *msg,
41 unsigned short len) {
42 if ((log_id >= LOG_ID_MAX) || (log_id < 0)) {
43 return;
44 }
45 LogBufferElement *elem = new LogBufferElement(log_id, realtime,
46 uid, pid, msg, len);
47
48 pthread_mutex_lock(&mLogElementsLock);
49
50 // Insert elements in time sorted order if possible
51 // NB: if end is region locked, place element at end of list
52 LogBufferElementCollection::iterator it = mLogElements.end();
53 LogBufferElementCollection::iterator last = it;
54 while (--it != mLogElements.begin()) {
55 if ((*it)->getRealTime() <= elem->getRealTime()) {
56 break;
57 }
58 last = it;
59 }
60 if (last == mLogElements.end()) {
61 mLogElements.push_back(elem);
62 } else {
63 log_time end;
64 bool end_set = false;
65 bool end_always = false;
66
67 LogTimeEntry::lock();
68
69 LastLogTimes::iterator t = mTimes.begin();
70 while(t != mTimes.end()) {
71 LogTimeEntry *entry = (*t);
72 if (entry->owned_Locked()) {
73 if (!entry->mNonBlock) {
74 end_always = true;
75 break;
76 }
77 if (!end_set || (end <= entry->mEnd)) {
78 end = entry->mEnd;
79 end_set = true;
80 }
81 }
82 t++;
83 }
84
85 if (end_always
86 || (end_set && (end >= (*last)->getMonotonicTime()))) {
87 mLogElements.push_back(elem);
88 } else {
89 mLogElements.insert(last,elem);
90 }
91
92 LogTimeEntry::unlock();
93 }
94
95 mSizes[log_id] += len;
96 mElements[log_id]++;
97 maybePrune(log_id);
98 pthread_mutex_unlock(&mLogElementsLock);
99}
100
101// If we're using more than 256K of memory for log entries, prune
102// 10% of the log entries.
103//
104// mLogElementsLock must be held when this function is called.
105void LogBuffer::maybePrune(log_id_t id) {
106 if (mSizes[id] > LOG_BUFFER_SIZE) {
107 prune(id, mElements[id] / 10);
108 }
109}
110
111// prune "pruneRows" of type "id" from the buffer.
112//
113// mLogElementsLock must be held when this function is called.
114void LogBuffer::prune(log_id_t id, unsigned long pruneRows) {
115 LogTimeEntry *oldest = NULL;
116
117 LogTimeEntry::lock();
118
119 // Region locked?
120 LastLogTimes::iterator t = mTimes.begin();
121 while(t != mTimes.end()) {
122 LogTimeEntry *entry = (*t);
123 if (entry->owned_Locked()
124 && (!oldest || (oldest->mStart > entry->mStart))) {
125 oldest = entry;
126 }
127 t++;
128 }
129
130 LogBufferElementCollection::iterator it = mLogElements.begin();
131 while((pruneRows > 0) && (it != mLogElements.end())) {
132 LogBufferElement *e = *it;
133 if (e->getLogId() == id) {
134 if (oldest && (oldest->mStart <= e->getMonotonicTime())) {
135 if (mSizes[id] > (2 * LOG_BUFFER_SIZE)) {
136 // kick a misbehaving log reader client off the island
137 oldest->release_Locked();
138 } else {
139 oldest->triggerSkip_Locked(pruneRows);
140 }
141 break;
142 }
143 it = mLogElements.erase(it);
144 mSizes[id] -= e->getMsgLen();
145 mElements[id]--;
146 delete e;
147 pruneRows--;
148 } else {
149 it++;
150 }
151 }
152
153 LogTimeEntry::unlock();
154}
155
156// clear all rows of type "id" from the buffer.
157void LogBuffer::clear(log_id_t id) {
158 pthread_mutex_lock(&mLogElementsLock);
159 prune(id, ULONG_MAX);
160 pthread_mutex_unlock(&mLogElementsLock);
161}
162
163// get the used space associated with "id".
164unsigned long LogBuffer::getSizeUsed(log_id_t id) {
165 pthread_mutex_lock(&mLogElementsLock);
166 unsigned long retval = mSizes[id];
167 pthread_mutex_unlock(&mLogElementsLock);
168 return retval;
169}
170
171// get the total space allocated to "id"
172unsigned long LogBuffer::getSize(log_id_t /*id*/) {
173 return LOG_BUFFER_SIZE;
174}
175
176struct timespec LogBuffer::flushTo(
177 SocketClient *reader, const struct timespec start, bool privileged,
178 bool (*filter)(const LogBufferElement *element, void *arg), void *arg) {
179 LogBufferElementCollection::iterator it;
180 log_time max = start;
181 uid_t uid = reader->getUid();
182
183 pthread_mutex_lock(&mLogElementsLock);
184 for (it = mLogElements.begin(); it != mLogElements.end(); ++it) {
185 LogBufferElement *element = *it;
186
187 if (!privileged && (element->getUid() != uid)) {
188 continue;
189 }
190
191 if (element->getMonotonicTime() <= start) {
192 continue;
193 }
194
195 // NB: calling out to another object with mLogElementsLock held (safe)
196 if (filter && !(*filter)(element, arg)) {
197 continue;
198 }
199
200 pthread_mutex_unlock(&mLogElementsLock);
201
202 // range locking in LastLogTimes looks after us
203 max = element->flushTo(reader);
204
205 if (max == element->FLUSH_ERROR) {
206 return max;
207 }
208
209 pthread_mutex_lock(&mLogElementsLock);
210 }
211 pthread_mutex_unlock(&mLogElementsLock);
212
213 return max;
214}
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
new file mode 100644
index 000000000..7c69f1bdc
--- /dev/null
+++ b/logd/LogBuffer.h
@@ -0,0 +1,60 @@
1/*
2 * Copyright (C) 2012-2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef _LOGD_LOG_BUFFER_H__
18#define _LOGD_LOG_BUFFER_H__
19
20#include <sys/types.h>
21
22#include <log/log.h>
23#include <sysutils/SocketClient.h>
24#include <utils/List.h>
25
26#include "LogBufferElement.h"
27#include "LogTimes.h"
28
29typedef android::List<LogBufferElement *> LogBufferElementCollection;
30
31class LogBuffer {
32 LogBufferElementCollection mLogElements;
33 pthread_mutex_t mLogElementsLock;
34
35 unsigned long mSizes[LOG_ID_MAX];
36 unsigned long mElements[LOG_ID_MAX];
37
38public:
39 LastLogTimes &mTimes;
40
41 LogBuffer(LastLogTimes *times);
42
43 void log(log_id_t log_id, struct timespec realtime,
44 uid_t uid, pid_t pid, const char *msg, unsigned short len);
45 struct timespec flushTo(SocketClient *writer, const struct timespec start,
46 bool privileged,
47 bool (*filter)(const LogBufferElement *element, void *arg) = NULL,
48 void *arg = NULL);
49
50 void clear(log_id_t id);
51 unsigned long getSize(log_id_t id);
52 unsigned long getSizeUsed(log_id_t id);
53
54private:
55 void maybePrune(log_id_t id);
56 void prune(log_id_t id, unsigned long pruneRows);
57
58};
59
60#endif
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
new file mode 100644
index 000000000..1c55623c3
--- /dev/null
+++ b/logd/LogBufferElement.cpp
@@ -0,0 +1,64 @@
1/*
2 * Copyright (C) 2012-2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdio.h>
18#include <string.h>
19#include <time.h>
20#include <unistd.h>
21
22#include <log/logger.h>
23
24#include "LogBufferElement.h"
25#include "LogReader.h"
26
27const struct timespec LogBufferElement::FLUSH_ERROR = { 0, 0 };
28
29LogBufferElement::LogBufferElement(log_id_t log_id, struct timespec realtime, uid_t uid, pid_t pid, const char *msg, unsigned short len)
30 : mLogId(log_id)
31 , mUid(uid)
32 , mPid(pid)
33 , mMsgLen(len)
34 , mMonotonicTime(CLOCK_MONOTONIC)
35 , mRealTime(realtime) {
36 mMsg = new char[len];
37 memcpy(mMsg, msg, len);
38}
39
40LogBufferElement::~LogBufferElement() {
41 delete [] mMsg;
42}
43
44struct timespec LogBufferElement::flushTo(SocketClient *reader) {
45 struct logger_entry_v3 entry;
46 memset(&entry, 0, sizeof(struct logger_entry_v3));
47 entry.hdr_size = sizeof(struct logger_entry_v3);
48 entry.len = mMsgLen;
49 entry.lid = mLogId;
50 entry.pid = mPid;
51 entry.sec = mRealTime.tv_sec;
52 entry.nsec = mRealTime.tv_nsec;
53
54 struct iovec iovec[2];
55 iovec[0].iov_base = &entry;
56 iovec[0].iov_len = sizeof(struct logger_entry_v3);
57 iovec[1].iov_base = mMsg;
58 iovec[1].iov_len = mMsgLen;
59 if (reader->sendDatav(iovec, 2)) {
60 return FLUSH_ERROR;
61 }
62
63 return mMonotonicTime;
64}
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
new file mode 100644
index 000000000..390c97c23
--- /dev/null
+++ b/logd/LogBufferElement.h
@@ -0,0 +1,50 @@
1/*
2 * Copyright (C) 2012-2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef _LOGD_LOG_BUFFER_ELEMENT_H__
18#define _LOGD_LOG_BUFFER_ELEMENT_H__
19
20#include <sys/types.h>
21#include <sysutils/SocketClient.h>
22#include <log/log.h>
23#include <log/log_read.h>
24
25class LogBufferElement {
26 const log_id_t mLogId;
27 const uid_t mUid;
28 const pid_t mPid;
29 char *mMsg;
30 const unsigned short mMsgLen;
31 const log_time mMonotonicTime;
32 const log_time mRealTime;
33
34public:
35 LogBufferElement(log_id_t log_id, struct timespec realtime,
36 uid_t uid, pid_t pid, const char *msg, unsigned short len);
37 virtual ~LogBufferElement();
38
39 log_id_t getLogId() const { return mLogId; }
40 uid_t getUid(void) const { return mUid; }
41 pid_t getPid(void) const { return mPid; }
42 unsigned short getMsgLen() const { return mMsgLen; }
43 log_time getMonotonicTime(void) const { return mMonotonicTime; }
44 log_time getRealTime(void) const { return mRealTime; }
45
46 static const struct timespec FLUSH_ERROR;
47 struct timespec flushTo(SocketClient *writer);
48};
49
50#endif
diff --git a/logd/LogCommand.cpp b/logd/LogCommand.cpp
new file mode 100644
index 000000000..ec8365aa1
--- /dev/null
+++ b/logd/LogCommand.cpp
@@ -0,0 +1,21 @@
1/*
2 * Copyright (C) 2012-2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "LogCommand.h"
18
19LogCommand::LogCommand(const char *cmd) :
20 FrameworkCommand(cmd) {
21}
diff --git a/logd/LogCommand.h b/logd/LogCommand.h
new file mode 100644
index 000000000..aef6706bd
--- /dev/null
+++ b/logd/LogCommand.h
@@ -0,0 +1,28 @@
1/*
2 * Copyright (C) 2012-2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef _LOGD_COMMAND_H
18#define _LOGD_COMMAND_H
19
20#include <sysutils/FrameworkCommand.h>
21
22class LogCommand : public FrameworkCommand {
23public:
24 LogCommand(const char *cmd);
25 virtual ~LogCommand() {}
26};
27
28#endif
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
new file mode 100644
index 000000000..c6b248bdc
--- /dev/null
+++ b/logd/LogListener.cpp
@@ -0,0 +1,108 @@
1/*
2 * Copyright (C) 2012-2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <sys/socket.h>
18#include <sys/types.h>
19#include <sys/un.h>
20#include <unistd.h>
21
22#include <cutils/sockets.h>
23#include <log/logger.h>
24
25#include "LogListener.h"
26
27LogListener::LogListener(LogBuffer *buf, LogReader *reader)
28 : SocketListener(getLogSocket(), false)
29 , logbuf(buf)
30 , reader(reader)
31{ }
32
33bool LogListener::onDataAvailable(SocketClient *cli) {
34 char buffer[1024];
35 struct iovec iov = { buffer, sizeof(buffer) };
36 memset(buffer, 0, sizeof(buffer));
37
38 char control[CMSG_SPACE(sizeof(struct ucred))];
39 struct msghdr hdr = {
40 NULL,
41 0,
42 &iov,
43 1,
44 control,
45 sizeof(control),
46 0,
47 };
48
49 int socket = cli->getSocket();
50
51 ssize_t n = recvmsg(socket, &hdr, 0);
52 if (n <= (ssize_t) sizeof_log_id_t) {
53 return false;
54 }
55
56 struct ucred *cred = NULL;
57
58 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);
59 while (cmsg != NULL) {
60 if (cmsg->cmsg_level == SOL_SOCKET
61 && cmsg->cmsg_type == SCM_CREDENTIALS) {
62 cred = (struct ucred *)CMSG_DATA(cmsg);
63 break;
64 }
65 cmsg = CMSG_NXTHDR(&hdr, cmsg);
66 }
67
68 if (cred == NULL) {
69 return false;
70 }
71
72 if (cred->uid == getuid()) {
73 // ignore log messages we send to ourself.
74 // Such log messages are often generated by libraries we depend on
75 // which use standard Android logging.
76 return false;
77 }
78
79 // First log element is always log_id.
80 log_id_t log_id = (log_id_t) *((typeof_log_id_t *) buffer);
81 if (log_id < 0 || log_id >= LOG_ID_MAX) {
82 return false;
83 }
84
85 char *msg = ((char *)buffer) + sizeof_log_id_t;
86 n -= sizeof_log_id_t;
87
88 log_time realtime(msg);
89 msg += sizeof(log_time);
90 n -= sizeof(log_time);
91
92 unsigned short len = n;
93 if (len == n) {
94 logbuf->log(log_id, realtime, cred->uid, cred->pid, msg, len);
95 reader->notifyNewLog();
96 }
97
98 return true;
99}
100
101int LogListener::getLogSocket() {
102 int sock = android_get_control_socket("logdw");
103 int on = 1;
104 if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
105 return -1;
106 }
107 return sock;
108}
diff --git a/logd/LogListener.h b/logd/LogListener.h
new file mode 100644
index 000000000..7099e1325
--- /dev/null
+++ b/logd/LogListener.h
@@ -0,0 +1,37 @@
1/*
2 * Copyright (C) 2012-2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef _LOGD_LOG_LISTENER_H__
18#define _LOGD_LOG_LISTENER_H__
19
20#include <sysutils/SocketListener.h>
21#include "LogReader.h"
22
23class LogListener : public SocketListener {
24 LogBuffer *logbuf;
25 LogReader *reader;
26
27public:
28 LogListener(LogBuffer *buf, LogReader *reader);
29
30protected:
31 virtual bool onDataAvailable(SocketClient *cli);
32
33private:
34 static int getLogSocket();
35};
36
37#endif
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
new file mode 100644
index 000000000..5b540bf5d
--- /dev/null
+++ b/logd/LogReader.cpp
@@ -0,0 +1,105 @@
1/*
2 * Copyright (C) 2012-2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <poll.h>
18#include <sys/socket.h>
19#include <cutils/sockets.h>
20
21#include "LogReader.h"
22#include "FlushCommand.h"
23
24LogReader::LogReader(LogBuffer *logbuf)
25 : SocketListener("logdr", true)
26 , mLogbuf(*logbuf)
27{ }
28
29// When we are notified a new log entry is available, inform
30// all of our listening sockets.
31void LogReader::notifyNewLog() {
32 FlushCommand command(*this);
33 runOnEachSocket(&command);
34}
35
36bool LogReader::onDataAvailable(SocketClient *cli) {
37 char buffer[255];
38
39 int len = read(cli->getSocket(), buffer, sizeof(buffer) - 1);
40 if (len <= 0) {
41 doSocketDelete(cli);
42 return false;
43 }
44 buffer[len] = '\0';
45
46 unsigned long tail = 0;
47 static const char _tail[] = " tail=";
48 char *cp = strstr(buffer, _tail);
49 if (cp) {
50 tail = atol(cp + sizeof(_tail) - 1);
51 }
52
53 unsigned int logMask = -1;
54 static const char _logIds[] = " lids=";
55 cp = strstr(buffer, _logIds);
56 if (cp) {
57 logMask = 0;
58 cp += sizeof(_logIds) - 1;
59 while (*cp && *cp != '\0') {
60 int val = 0;
61 while (('0' <= *cp) && (*cp <= '9')) {
62 val *= 10;
63 val += *cp - '0';
64 ++cp;
65 }
66 logMask |= 1 << val;
67 if (*cp != ',') {
68 break;
69 }
70 ++cp;
71 }
72 }
73
74 pid_t pid = 0;
75 static const char _pid[] = " pid=";
76 cp = strstr(buffer, _pid);
77 if (cp) {
78 pid = atol(cp + sizeof(_pid) - 1);
79 }
80
81 bool nonBlock = false;
82 if (strncmp(buffer, "dumpAndClose", 12) == 0) {
83 nonBlock = true;
84 }
85
86 FlushCommand command(*this, nonBlock, tail, logMask, pid);
87 command.runSocketCommand(cli);
88 return true;
89}
90
91void LogReader::doSocketDelete(SocketClient *cli) {
92 LastLogTimes &times = mLogbuf.mTimes;
93 LogTimeEntry::lock();
94 LastLogTimes::iterator it = times.begin();
95 while(it != times.end()) {
96 LogTimeEntry *entry = (*it);
97 if (entry->mClient == cli) {
98 times.erase(it);
99 entry->release_Locked();
100 break;
101 }
102 it++;
103 }
104 LogTimeEntry::unlock();
105}
diff --git a/logd/LogReader.h b/logd/LogReader.h
new file mode 100644
index 000000000..b267c75d2
--- /dev/null
+++ b/logd/LogReader.h
@@ -0,0 +1,41 @@
1/*
2 * Copyright (C) 2012-2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef _LOGD_LOG_WRITER_H__
18#define _LOGD_LOG_WRITER_H__
19
20#include <sysutils/SocketListener.h>
21#include "LogBuffer.h"
22#include "LogTimes.h"
23
24class LogReader : public SocketListener {
25 LogBuffer &mLogbuf;
26
27public:
28 LogReader(LogBuffer *logbuf);
29 void notifyNewLog();
30
31 LogBuffer &logbuf(void) const { return mLogbuf; }
32
33protected:
34 virtual bool onDataAvailable(SocketClient *cli);
35
36private:
37 void doSocketDelete(SocketClient *cli);
38
39};
40
41#endif
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
new file mode 100644
index 000000000..d6d4e9331
--- /dev/null
+++ b/logd/LogTimes.cpp
@@ -0,0 +1,225 @@
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "FlushCommand.h"
18#include "LogBuffer.h"
19#include "LogTimes.h"
20#include "LogReader.h"
21
22pthread_mutex_t LogTimeEntry::timesLock = PTHREAD_MUTEX_INITIALIZER;
23
24const struct timespec LogTimeEntry::EPOCH = { 0, 1 };
25
26LogTimeEntry::LogTimeEntry(LogReader &reader, SocketClient *client,
27 bool nonBlock, unsigned long tail,
28 unsigned int logMask, pid_t pid)
29 : mRefCount(1)
30 , mRelease(false)
31 , mError(false)
32 , threadRunning(false)
33 , threadTriggered(true)
34 , mReader(reader)
35 , mLogMask(logMask)
36 , mPid(pid)
37 , skipAhead(0)
38 , mCount(0)
39 , mTail(tail)
40 , mIndex(0)
41 , mClient(client)
42 , mStart(EPOCH)
43 , mNonBlock(nonBlock)
44 , mEnd(CLOCK_MONOTONIC)
45{ }
46
47void LogTimeEntry::startReader_Locked(void) {
48 threadRunning = true;
49 if (pthread_create(&mThread, NULL, LogTimeEntry::threadStart, this)) {
50 threadRunning = false;
51 if (mClient) {
52 mClient->decRef();
53 }
54 decRef_Locked();
55 }
56}
57
58void LogTimeEntry::threadStop(void *obj) {
59 LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
60
61 lock();
62
63 me->threadRunning = false;
64 if (me->mNonBlock) {
65 me->error_Locked();
66 }
67
68 SocketClient *client = me->mClient;
69
70 if (me->isError_Locked()) {
71 LogReader &reader = me->mReader;
72 LastLogTimes &times = reader.logbuf().mTimes;
73
74 LastLogTimes::iterator it = times.begin();
75 while(it != times.end()) {
76 if (*it == me) {
77 times.erase(it);
78 me->release_Locked();
79 break;
80 }
81 it++;
82 }
83
84 me->mClient = NULL;
85 reader.release(client);
86 }
87
88 if (client) {
89 client->decRef();
90 }
91
92 me->decRef_Locked();
93
94 unlock();
95}
96
97void *LogTimeEntry::threadStart(void *obj) {
98 LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
99
100 pthread_cleanup_push(threadStop, obj);
101
102 SocketClient *client = me->mClient;
103 if (!client) {
104 me->error();
105 pthread_exit(NULL);
106 }
107
108 LogBuffer &logbuf = me->mReader.logbuf();
109
110 bool privileged = FlushCommand::hasReadLogs(client);
111
112 lock();
113
114 me->threadTriggered = true;
115
116 while(me->threadTriggered && !me->isError_Locked()) {
117
118 me->threadTriggered = false;
119
120 log_time start = me->mStart;
121
122 unlock();
123
124 if (me->mTail) {
125 logbuf.flushTo(client, start, privileged, FilterFirstPass, me);
126 }
127 start = logbuf.flushTo(client, start, privileged, FilterSecondPass, me);
128
129 if (start == LogBufferElement::FLUSH_ERROR) {
130 me->error();
131 }
132
133 if (me->mNonBlock) {
134 lock();
135 break;
136 }
137
138 sched_yield();
139
140 lock();
141 }
142
143 unlock();
144
145 pthread_exit(NULL);
146
147 pthread_cleanup_pop(true);
148
149 return NULL;
150}
151
152// A first pass to count the number of elements
153bool LogTimeEntry::FilterFirstPass(const LogBufferElement *element, void *obj) {
154 LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
155
156 LogTimeEntry::lock();
157
158 if (me->mCount == 0) {
159 me->mStart = element->getMonotonicTime();
160 }
161
162 if ((!me->mPid || (me->mPid == element->getPid()))
163 && (me->mLogMask & (1 << element->getLogId()))) {
164 ++me->mCount;
165 }
166
167 LogTimeEntry::unlock();
168
169 return false;
170}
171
172// A second pass to send the selected elements
173bool LogTimeEntry::FilterSecondPass(const LogBufferElement *element, void *obj) {
174 LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
175
176 LogTimeEntry::lock();
177
178 if (me->skipAhead) {
179 me->skipAhead--;
180 }
181
182 me->mStart = element->getMonotonicTime();
183
184 // Truncate to close race between first and second pass
185 if (me->mNonBlock && me->mTail && (me->mIndex >= me->mCount)) {
186 goto skip;
187 }
188
189 if ((me->mLogMask & (1 << element->getLogId())) == 0) {
190 goto skip;
191 }
192
193 if (me->mPid && (me->mPid != element->getPid())) {
194 goto skip;
195 }
196
197 if (me->isError_Locked()) {
198 goto skip;
199 }
200
201 if (!me->mTail) {
202 goto ok;
203 }
204
205 ++me->mIndex;
206
207 if ((me->mCount > me->mTail) && (me->mIndex <= (me->mCount - me->mTail))) {
208 goto skip;
209 }
210
211 if (!me->mNonBlock) {
212 me->mTail = 0;
213 }
214
215ok:
216 if (!me->skipAhead) {
217 LogTimeEntry::unlock();
218 return true;
219 }
220 // FALLTHRU
221
222skip:
223 LogTimeEntry::unlock();
224 return false;
225}
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
new file mode 100644
index 000000000..ac52db23d
--- /dev/null
+++ b/logd/LogTimes.h
@@ -0,0 +1,108 @@
1/*
2 * Copyright (C) 2012-2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef _LOGD_LOG_TIMES_H__
18#define _LOGD_LOG_TIMES_H__
19
20#include <pthread.h>
21#include <time.h>
22#include <sys/types.h>
23#include <sysutils/SocketClient.h>
24#include <utils/List.h>
25
26class LogReader;
27
28class LogTimeEntry {
29 static pthread_mutex_t timesLock;
30 unsigned int mRefCount;
31 bool mRelease;
32 bool mError;
33 bool threadRunning;
34 bool threadTriggered;
35 pthread_t mThread;
36 LogReader &mReader;
37 static void *threadStart(void *me);
38 static void threadStop(void *me);
39 const unsigned int mLogMask;
40 const pid_t mPid;
41 unsigned int skipAhead;
42 unsigned long mCount;
43 unsigned long mTail;
44 unsigned long mIndex;
45
46public:
47 LogTimeEntry(LogReader &reader, SocketClient *client, bool nonBlock,
48 unsigned long tail, unsigned int logMask, pid_t pid);
49
50 SocketClient *mClient;
51 static const struct timespec EPOCH;
52 log_time mStart;
53 const bool mNonBlock;
54 const log_time mEnd; // only relevant if mNonBlock
55
56 // Protect List manipulations
57 static void lock(void) { pthread_mutex_lock(&timesLock); }
58 static void unlock(void) { pthread_mutex_unlock(&timesLock); }
59
60 void startReader_Locked(void);
61
62 bool runningReader_Locked(void) const
63 {
64 return threadRunning || mRelease || mError || mNonBlock;
65 }
66 void triggerReader_Locked(void) { threadTriggered = true; }
67 void triggerSkip_Locked(unsigned int skip) { skipAhead = skip; }
68
69 // Called after LogTimeEntry removed from list, lock implicitly held
70 void release_Locked(void)
71 {
72 mRelease = true;
73 if (mRefCount || threadRunning) {
74 return;
75 }
76 // No one else is holding a reference to this
77 delete this;
78 }
79
80 // Called to mark socket in jeopardy
81 void error_Locked(void) { mError = true; }
82 void error(void) { lock(); mError = true; unlock(); }
83
84 bool isError_Locked(void) const { return mRelease || mError; }
85
86 // Mark Used
87 // Locking implied, grabbed when protection around loop iteration
88 void incRef_Locked(void) { ++mRefCount; }
89
90 bool owned_Locked(void) const { return mRefCount != 0; }
91
92 void decRef_Locked(void)
93 {
94 if ((mRefCount && --mRefCount) || !mRelease || threadRunning) {
95 return;
96 }
97 // No one else is holding a reference to this
98 delete this;
99 }
100
101 // flushTo filter callbacks
102 static bool FilterFirstPass(const LogBufferElement *element, void *me);
103 static bool FilterSecondPass(const LogBufferElement *element, void *me);
104};
105
106typedef android::List<LogTimeEntry *> LastLogTimes;
107
108#endif
diff --git a/logd/main.cpp b/logd/main.cpp
new file mode 100644
index 000000000..667e5bbfc
--- /dev/null
+++ b/logd/main.cpp
@@ -0,0 +1,115 @@
1/*
2 * Copyright (C) 2012-2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <dirent.h>
18#include <errno.h>
19#include <fcntl.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <sys/capability.h>
24#include <sys/stat.h>
25#include <sys/types.h>
26
27#include <linux/prctl.h>
28
29#include "private/android_filesystem_config.h"
30#include "CommandListener.h"
31#include "LogBuffer.h"
32#include "LogListener.h"
33
34static int drop_privs() {
35 if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
36 return -1;
37 }
38
39 if (setgid(AID_LOGD) != 0) {
40 return -1;
41 }
42
43 if (setuid(AID_LOGD) != 0) {
44 return -1;
45 }
46
47 struct __user_cap_header_struct capheader;
48 struct __user_cap_data_struct capdata[2];
49 memset(&capheader, 0, sizeof(capheader));
50 memset(&capdata, 0, sizeof(capdata));
51 capheader.version = _LINUX_CAPABILITY_VERSION_3;
52 capheader.pid = 0;
53
54 capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
55 capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
56 capdata[0].inheritable = 0;
57 capdata[1].inheritable = 0;
58
59 if (capset(&capheader, &capdata[0]) < 0) {
60 return -1;
61 }
62
63 return 0;
64}
65
66// Foreground waits for exit of the three main persistent threads that
67// are started here. The three threads are created to manage UNIX
68// domain client sockets for writing, reading and controlling the user
69// space logger. Additional transitory per-client threads are created
70// for each reader once they register.
71int main() {
72 if (drop_privs() != 0) {
73 return -1;
74 }
75
76 // Serves the purpose of managing the last logs times read on a
77 // socket connection, and as a reader lock on a range of log
78 // entries.
79
80 LastLogTimes *times = new LastLogTimes();
81
82 // LogBuffer is the object which is responsible for holding all
83 // log entries.
84
85 LogBuffer *logBuf = new LogBuffer(times);
86
87 // LogReader listens on /dev/socket/logdr. When a client
88 // connects, log entries in the LogBuffer are written to the client.
89
90 LogReader *reader = new LogReader(logBuf);
91 if (reader->startListener()) {
92 exit(1);
93 }
94
95 // LogListener listens on /dev/socket/logdw for client
96 // initiated log messages. New log entries are added to LogBuffer
97 // and LogReader is notified to send updates to connected clients.
98
99 LogListener *swl = new LogListener(logBuf, reader);
100 if (swl->startListener()) {
101 exit(1);
102 }
103
104 // Command listener listens on /dev/socket/logd for incoming logd
105 // administrative commands.
106
107 CommandListener *cl = new CommandListener(logBuf, reader, swl);
108 if (cl->startListener()) {
109 exit(1);
110 }
111
112 pause();
113 exit(0);
114}
115