blob: 35fe3a5e9cb7221c8969cb90599f1125ddf8666d [file] [log] [blame]
Mark Salyzynae4d9282014-10-15 08:49:39 -07001/*
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 <ctype.h>
18#include <errno.h>
19#include <inttypes.h>
20#include <limits.h>
21#include <stdarg.h>
22#include <stdlib.h>
23#include <sys/prctl.h>
24#include <sys/uio.h>
25#include <syslog.h>
26
27#include <log/logger.h>
28
29#include "LogKlog.h"
30
31#define KMSG_PRIORITY(PRI) \
32 '<', \
33 '0' + (LOG_SYSLOG | (PRI)) / 10, \
34 '0' + (LOG_SYSLOG | (PRI)) % 10, \
35 '>'
36
37static const char priority_message[] = { KMSG_PRIORITY(LOG_INFO), '\0' };
38
39log_time LogKlog::correction = log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC);
40
41LogKlog::LogKlog(LogBuffer *buf, LogReader *reader, int fdWrite, int fdRead, bool auditd)
42 : SocketListener(fdRead, false)
43 , logbuf(buf)
44 , reader(reader)
45 , signature(CLOCK_MONOTONIC)
46 , fdWrite(fdWrite)
47 , fdRead(fdRead)
48 , initialized(false)
49 , enableLogging(true)
50 , auditd(auditd) {
51 static const char klogd_message[] = "%slogd.klogd: %" PRIu64 "\n";
52 char buffer[sizeof(priority_message) + sizeof(klogd_message) + 20 - 4];
53 snprintf(buffer, sizeof(buffer), klogd_message, priority_message,
54 signature.nsec());
55 write(fdWrite, buffer, strlen(buffer));
56}
57
58bool LogKlog::onDataAvailable(SocketClient *cli) {
59 if (!initialized) {
60 prctl(PR_SET_NAME, "logd.klogd");
61 initialized = true;
62 enableLogging = false;
63 }
64
65 char buffer[LOGGER_ENTRY_MAX_PAYLOAD];
66 size_t len = 0;
67
68 for(;;) {
69 ssize_t retval = 0;
70 if ((sizeof(buffer) - 1 - len) > 0) {
71 retval = read(cli->getSocket(), buffer + len, sizeof(buffer) - 1 - len);
72 }
73 if ((retval == 0) && (len == 0)) {
74 break;
75 }
76 if (retval < 0) {
77 return false;
78 }
79 len += retval;
80 bool full = len == (sizeof(buffer) - 1);
81 char *ep = buffer + len;
82 *ep = '\0';
83 len = 0;
84 for(char *ptr, *tok = buffer;
85 ((tok = strtok_r(tok, "\r\n", &ptr)));
86 tok = NULL) {
87 if (((tok + strlen(tok)) == ep) && (retval != 0) && full) {
88 len = strlen(tok);
89 memmove(buffer, tok, len);
90 break;
91 }
92 if (*tok) {
93 log(tok);
94 }
95 }
96 }
97
98 return true;
99}
100
101
102void LogKlog::calculateCorrection(const log_time &monotonic,
103 const char *real_string) {
104 log_time real;
105 if (!real.strptime(real_string, "%Y-%m-%d %H:%M:%S.%09q UTC")) {
106 return;
107 }
108 // kernel report UTC, log_time::strptime is localtime from calendar.
109 // Bionic and liblog strptime does not support %z or %Z to pick up
110 // timezone so we are calculating our own correction.
111 time_t now = real.tv_sec;
112 struct tm tm;
113 memset(&tm, 0, sizeof(tm));
114 tm.tm_isdst = -1;
115 localtime_r(&now, &tm);
116 real.tv_sec += tm.tm_gmtoff;
117 correction = real - monotonic;
118}
119
120void LogKlog::sniffTime(log_time &now, const char **buf, bool reverse) {
121 const char *cp;
122 if ((cp = now.strptime(*buf, "[ %s.%q]"))) {
123 static const char suspend[] = "PM: suspend entry ";
124 static const char resume[] = "PM: suspend exit ";
125 static const char suspended[] = "Suspended for ";
126
127 if (isspace(*cp)) {
128 ++cp;
129 }
130 if (!strncmp(cp, suspend, sizeof(suspend) - 1)) {
131 calculateCorrection(now, cp + sizeof(suspend) - 1);
132 } else if (!strncmp(cp, resume, sizeof(resume) - 1)) {
133 calculateCorrection(now, cp + sizeof(resume) - 1);
134 } else if (!strncmp(cp, suspended, sizeof(suspended) - 1)) {
135 log_time real;
136 char *endp;
137 real.tv_sec = strtol(cp + sizeof(suspended) - 1, &endp, 10);
138 if (*endp == '.') {
139 real.tv_nsec = strtol(endp + 1, &endp, 10) * 1000000L;
140 if (reverse) {
141 correction -= real;
142 } else {
143 correction += real;
144 }
145 }
146 }
147
148 convertMonotonicToReal(now);
149 *buf = cp;
150 } else {
151 now = log_time(CLOCK_REALTIME);
152 }
153}
154
155// Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a
156// compensated start time.
157void LogKlog::synchronize(const char *buf) {
158 const char *cp = strstr(buf, "] PM: suspend e");
159 if (!cp) {
160 return;
161 }
162
163 do {
164 --cp;
165 } while ((cp > buf) && (isdigit(*cp) || isspace(*cp) || (*cp == '.')));
166
167 log_time now;
168 sniffTime(now, &cp, true);
169
170 char *suspended = strstr(buf, "] Suspended for ");
171 if (!suspended || (suspended > cp)) {
172 return;
173 }
174 cp = suspended;
175
176 do {
177 --cp;
178 } while ((cp > buf) && (isdigit(*cp) || isspace(*cp) || (*cp == '.')));
179
180 sniffTime(now, &cp, true);
181}
182
183// kernel log prefix, convert to a kernel log priority number
184static int parseKernelPrio(const char **buf) {
185 int pri = LOG_USER | LOG_INFO;
186 const char *cp = *buf;
187 if (*cp == '<') {
188 pri = 0;
189 while(isdigit(*++cp)) {
190 pri = (pri * 10) + *cp - '0';
191 }
192 if (*cp == '>') {
193 ++cp;
194 } else {
195 cp = *buf;
196 pri = LOG_USER | LOG_INFO;
197 }
198 *buf = cp;
199 }
200 return pri;
201}
202
203// Convert kernel log priority number into an Android Logger priority number
204static int convertKernelPrioToAndroidPrio(int pri) {
205 switch(pri & LOG_PRIMASK) {
206 case LOG_EMERG:
207 // FALLTHRU
208 case LOG_ALERT:
209 // FALLTHRU
210 case LOG_CRIT:
211 return ANDROID_LOG_FATAL;
212
213 case LOG_ERR:
214 return ANDROID_LOG_ERROR;
215
216 case LOG_WARNING:
217 return ANDROID_LOG_WARN;
218
219 default:
220 // FALLTHRU
221 case LOG_NOTICE:
222 // FALLTHRU
223 case LOG_INFO:
224 break;
225
226 case LOG_DEBUG:
227 return ANDROID_LOG_DEBUG;
228 }
229
230 return ANDROID_LOG_INFO;
231}
232
233//
234// log a message into the kernel log buffer
235//
236// Filter rules to parse <PRI> <TIME> <tag> and <message> in order for
237// them to appear correct in the logcat output:
238//
239// LOG_KERN (0):
240// <PRI>[<TIME>] <tag> ":" <message>
241// <PRI>[<TIME>] <tag> <tag> ":" <message>
242// <PRI>[<TIME>] <tag> <tag>_work ":" <message>
243// <PRI>[<TIME>] <tag> '<tag>.<num>' ":" <message>
244// <PRI>[<TIME>] <tag> '<tag><num>' ":" <message>
245// <PRI>[<TIME>] <tag>_host '<tag>.<num>' ":" <message>
246// (unimplemented) <PRI>[<TIME>] <tag> '<num>.<tag>' ":" <message>
247// <PRI>[<TIME>] "[INFO]"<tag> : <message>
248// <PRI>[<TIME>] "------------[ cut here ]------------" (?)
249// <PRI>[<TIME>] "---[ end trace 3225a3070ca3e4ac ]---" (?)
250// LOG_USER, LOG_MAIL, LOG_DAEMON, LOG_AUTH, LOG_SYSLOG, LOG_LPR, LOG_NEWS
251// LOG_UUCP, LOG_CRON, LOG_AUTHPRIV, LOG_FTP:
252// <PRI+TAG>[<TIME>] (see sys/syslog.h)
253// Observe:
254// Minimum tag length = 3 NB: drops things like r5:c00bbadf, but allow PM:
255// Maximum tag words = 2
256// Maximum tag length = 16 NB: we are thinking of how ugly logcat can get.
257// Not a Tag if there is no message content.
258// leading additional spaces means no tag, inherit last tag.
259// Not a Tag if <tag>: is "ERROR:", "WARNING:", "INFO:" or "CPU:"
260// Drop:
261// empty messages
262// messages with ' audit(' in them if auditd is running
263// logd.klogd:
264// return -1 if message logd.klogd: <signature>
265//
266int LogKlog::log(const char *buf) {
267 if (auditd && strstr(buf, " audit(")) {
268 return 0;
269 }
270
271 int pri = parseKernelPrio(&buf);
272
273 log_time now;
274 sniffTime(now, &buf, false);
275
276 // sniff for start marker
277 const char klogd_message[] = "logd.klogd: ";
278 if (!strncmp(buf, klogd_message, sizeof(klogd_message) - 1)) {
279 char *endp;
280 uint64_t sig = strtoll(buf + sizeof(klogd_message) - 1, &endp, 10);
281 if (sig == signature.nsec()) {
282 if (initialized) {
283 enableLogging = true;
284 } else {
285 enableLogging = false;
286 }
287 return -1;
288 }
289 return 0;
290 }
291
292 if (!enableLogging) {
293 return 0;
294 }
295
296 // Parse pid, tid and uid (not possible)
297 const pid_t pid = 0;
298 const pid_t tid = 0;
299 const uid_t uid = 0;
300
301 // Parse (rules at top) to pull out a tag from the incoming kernel message.
302 // Some may view the following as an ugly heuristic, the desire is to
303 // beautify the kernel logs into an Android Logging format; the goal is
304 // admirable but costly.
305 while (isspace(*buf)) {
306 ++buf;
307 }
308 if (!*buf) {
309 return 0;
310 }
311 const char *start = buf;
312 const char *tag = "";
313 const char *etag = tag;
314 if (!isspace(*buf)) {
315 const char *bt, *et, *cp;
316
317 bt = buf;
318 if (!strncmp(buf, "[INFO]", 6)) {
319 // <PRI>[<TIME>] "[INFO]"<tag> ":" message
320 bt = buf + 6;
321 }
322 for(et = bt; *et && (*et != ':') && !isspace(*et); ++et);
323 for(cp = et; isspace(*cp); ++cp);
324 size_t size;
325
326 if (*cp == ':') {
327 // One Word
328 tag = bt;
329 etag = et;
330 buf = cp + 1;
331 } else {
332 size = et - bt;
333 if (strncmp(bt, cp, size)) {
334 // <PRI>[<TIME>] <tag>_host '<tag>.<num>' : message
335 if (!strncmp(bt + size - 5, "_host", 5)
336 && !strncmp(bt, cp, size - 5)) {
337 const char *b = cp;
338 cp += size - 5;
339 if (*cp == '.') {
340 while (!isspace(*++cp) && (*cp != ':'));
341 const char *e;
342 for(e = cp; isspace(*cp); ++cp);
343 if (*cp == ':') {
344 tag = b;
345 etag = e;
346 buf = cp + 1;
347 }
348 }
349 } else {
350 while (!isspace(*++cp) && (*cp != ':'));
351 const char *e;
352 for(e = cp; isspace(*cp); ++cp);
353 // Two words
354 if (*cp == ':') {
355 tag = bt;
356 etag = e;
357 buf = cp + 1;
358 }
359 }
360 } else if (isspace(cp[size])) {
361 const char *b = cp;
362 cp += size;
363 while (isspace(*++cp));
364 // <PRI>[<TIME>] <tag> <tag> : message
365 if (*cp == ':') {
366 tag = bt;
367 etag = et;
368 buf = cp + 1;
369 }
370 } else if (cp[size] == ':') {
371 // <PRI>[<TIME>] <tag> <tag> : message
372 tag = bt;
373 etag = et;
374 buf = cp + size + 1;
375 } else if ((cp[size] == '.') || isdigit(cp[size])) {
376 // <PRI>[<TIME>] <tag> '<tag>.<num>' : message
377 // <PRI>[<TIME>] <tag> '<tag><num>' : message
378 const char *b = cp;
379 cp += size;
380 while (!isspace(*++cp) && (*cp != ':'));
381 const char *e = cp;
382 while (isspace(*cp)) {
383 ++cp;
384 }
385 if (*cp == ':') {
386 tag = b;
387 etag = e;
388 buf = cp + 1;
389 }
390 } else {
391 while (!isspace(*++cp) && (*cp != ':'));
392 const char *e = cp;
393 while (isspace(*cp)) {
394 ++cp;
395 }
396 // Two words
397 if (*cp == ':') {
398 tag = bt;
399 etag = e;
400 buf = cp + 1;
401 }
402 }
403 }
404 size = etag - tag;
405 if ((size <= 1)
406 || ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1])))
407 || ((size == 3) && !strncmp(tag, "CPU", 3))
408 || ((size == 7) && !strncmp(tag, "WARNING", 7))
409 || ((size == 5) && !strncmp(tag, "ERROR", 5))
410 || ((size == 4) && !strncmp(tag, "INFO", 4))) {
411 buf = start;
412 etag = tag = "";
413 }
414 }
415 size_t l = etag - tag;
416 while (isspace(*buf)) {
417 ++buf;
418 }
419 size_t n = 1 + l + 1 + strlen(buf) + 1;
420
421 // Allocate a buffer to hold the interpreted log message
422 int rc = n;
423 char *newstr = reinterpret_cast<char *>(malloc(n));
424 if (!newstr) {
425 rc = -ENOMEM;
426 return rc;
427 }
428 char *np = newstr;
429
430 // Convert priority into single-byte Android logger priority
431 *np = convertKernelPrioToAndroidPrio(pri);
432 ++np;
433
434 // Copy parsed tag following priority
435 strncpy(np, tag, l);
436 np += l;
437 *np = '\0';
438 ++np;
439
440 // Copy main message to the remainder
441 strcpy(np, buf);
442
443 // Log message
444 rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr,
445 (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX);
446 free(newstr);
447
448 // notify readers
449 if (!rc) {
450 reader->notifyNewLog();
451 }
452
453 return rc;
454}