blob: 4e0e89611e8192d3c109603961b7558d9d4009f7 [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
Mark Salyzyn2c3b3002015-05-22 15:23:44 -070039// Parsing is hard
40
41// called if we see a '<', s is the next character, returns pointer after '>'
42static char *is_prio(char *s) {
43 if (!isdigit(*s++)) {
44 return NULL;
45 }
46 char c;
47 while ((c = *s++)) {
48 if (!isdigit(c) && (c == '>')) {
49 return s;
50 }
51 }
52 return NULL;
53}
54
55// called if we see a '[', s is the next character, returns pointer after ']'
56static char *is_timestamp(char *s) {
57 while (*s == ' ') {
58 ++s;
59 }
60 if (!isdigit(*s++)) {
61 return NULL;
62 }
63 bool first_period = true;
64 char c;
65 while ((c = *s++)) {
66 if ((c == '.') && first_period) {
67 first_period = false;
68 continue;
69 }
70 if (!isdigit(c) && (c == ']')) {
Mark Salyzynd3c50932015-06-12 14:59:42 -070071 if (first_period || (*s != ' ')) {
72 break;
73 }
Mark Salyzyn2c3b3002015-05-22 15:23:44 -070074 return s;
75 }
76 }
77 return NULL;
78}
79
80// Like strtok_r with "\r\n" except that we look for log signatures (regex)
Mark Salyzynd3c50932015-06-12 14:59:42 -070081// \(\(<[0-9]+>\)\([[] *[0-9]+[.][0-9]+[]] \)\{0,1\}\|[[] *[0-9]+[.][0-9]+[]] \)
Mark Salyzyn2c3b3002015-05-22 15:23:44 -070082// and split if we see a second one without a newline.
83
84#define SIGNATURE_MASK 0xF0
85// <digit> following ('0' to '9' masked with ~SIGNATURE_MASK) added to signature
86#define LESS_THAN_SIG SIGNATURE_MASK
87#define OPEN_BRACKET_SIG ((SIGNATURE_MASK << 1) & SIGNATURE_MASK)
88// space is one more than <digit> of 9
Mark Salyzyn3e21de22015-06-08 14:51:30 -070089#define OPEN_BRACKET_SPACE ((char)(OPEN_BRACKET_SIG | 10))
Mark Salyzyn2c3b3002015-05-22 15:23:44 -070090
91char *log_strtok_r(char *s, char **last) {
92 if (!s) {
93 if (!(s = *last)) {
94 return NULL;
95 }
96 // fixup for log signature split <,
97 // LESS_THAN_SIG + <digit>
98 if ((*s & SIGNATURE_MASK) == LESS_THAN_SIG) {
99 *s = (*s & ~SIGNATURE_MASK) + '0';
100 *--s = '<';
101 }
102 // fixup for log signature split [,
103 // OPEN_BRACKET_SPACE is space, OPEN_BRACKET_SIG + <digit>
104 if ((*s & SIGNATURE_MASK) == OPEN_BRACKET_SIG) {
105 if (*s == OPEN_BRACKET_SPACE) {
106 *s = ' ';
107 } else {
108 *s = (*s & ~SIGNATURE_MASK) + '0';
109 }
110 *--s = '[';
111 }
112 }
113
114 s += strspn(s, "\r\n");
115
116 if (!*s) { // no non-delimiter characters
117 *last = NULL;
118 return NULL;
119 }
120 char *peek, *tok = s;
121
122 for (;;) {
123 char c = *s++;
124 switch (c) {
125 case '\0':
126 *last = NULL;
127 return tok;
128
129 case '\r':
130 case '\n':
131 s[-1] = '\0';
132 *last = s;
133 return tok;
134
135 case '<':
136 peek = is_prio(s);
137 if (!peek) {
138 break;
139 }
140 if (s != (tok + 1)) { // not first?
141 s[-1] = '\0';
142 *s &= ~SIGNATURE_MASK;
143 *s |= LESS_THAN_SIG; // signature for '<'
144 *last = s;
145 return tok;
146 }
147 s = peek;
148 if ((*s == '[') && ((peek = is_timestamp(s + 1)))) {
149 s = peek;
150 }
151 break;
152
153 case '[':
154 peek = is_timestamp(s);
155 if (!peek) {
156 break;
157 }
158 if (s != (tok + 1)) { // not first?
159 s[-1] = '\0';
160 if (*s == ' ') {
161 *s = OPEN_BRACKET_SPACE;
162 } else {
163 *s &= ~SIGNATURE_MASK;
164 *s |= OPEN_BRACKET_SIG; // signature for '['
165 }
166 *last = s;
167 return tok;
168 }
169 s = peek;
170 break;
171 }
172 }
173 /* NOTREACHED */
174}
175
Mark Salyzynae4d9282014-10-15 08:49:39 -0700176log_time LogKlog::correction = log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC);
177
Mark Salyzyn77187782015-05-12 15:21:31 -0700178LogKlog::LogKlog(LogBuffer *buf, LogReader *reader, int fdWrite, int fdRead, bool auditd) :
179 SocketListener(fdRead, false),
180 logbuf(buf),
181 reader(reader),
182 signature(CLOCK_MONOTONIC),
183 fdWrite(fdWrite),
184 fdRead(fdRead),
185 initialized(false),
186 enableLogging(true),
187 auditd(auditd) {
Mark Salyzynae4d9282014-10-15 08:49:39 -0700188 static const char klogd_message[] = "%slogd.klogd: %" PRIu64 "\n";
189 char buffer[sizeof(priority_message) + sizeof(klogd_message) + 20 - 4];
190 snprintf(buffer, sizeof(buffer), klogd_message, priority_message,
191 signature.nsec());
192 write(fdWrite, buffer, strlen(buffer));
193}
194
195bool LogKlog::onDataAvailable(SocketClient *cli) {
196 if (!initialized) {
197 prctl(PR_SET_NAME, "logd.klogd");
198 initialized = true;
199 enableLogging = false;
200 }
201
202 char buffer[LOGGER_ENTRY_MAX_PAYLOAD];
203 size_t len = 0;
204
205 for(;;) {
206 ssize_t retval = 0;
207 if ((sizeof(buffer) - 1 - len) > 0) {
208 retval = read(cli->getSocket(), buffer + len, sizeof(buffer) - 1 - len);
209 }
210 if ((retval == 0) && (len == 0)) {
211 break;
212 }
213 if (retval < 0) {
214 return false;
215 }
216 len += retval;
217 bool full = len == (sizeof(buffer) - 1);
218 char *ep = buffer + len;
219 *ep = '\0';
220 len = 0;
Mark Salyzyn2c3b3002015-05-22 15:23:44 -0700221 for(char *ptr = NULL, *tok = buffer;
222 ((tok = log_strtok_r(tok, &ptr)));
Mark Salyzynae4d9282014-10-15 08:49:39 -0700223 tok = NULL) {
224 if (((tok + strlen(tok)) == ep) && (retval != 0) && full) {
225 len = strlen(tok);
226 memmove(buffer, tok, len);
227 break;
228 }
229 if (*tok) {
230 log(tok);
231 }
232 }
233 }
234
235 return true;
236}
237
238
239void LogKlog::calculateCorrection(const log_time &monotonic,
240 const char *real_string) {
241 log_time real;
242 if (!real.strptime(real_string, "%Y-%m-%d %H:%M:%S.%09q UTC")) {
243 return;
244 }
245 // kernel report UTC, log_time::strptime is localtime from calendar.
246 // Bionic and liblog strptime does not support %z or %Z to pick up
247 // timezone so we are calculating our own correction.
248 time_t now = real.tv_sec;
249 struct tm tm;
250 memset(&tm, 0, sizeof(tm));
251 tm.tm_isdst = -1;
252 localtime_r(&now, &tm);
253 real.tv_sec += tm.tm_gmtoff;
254 correction = real - monotonic;
255}
256
257void LogKlog::sniffTime(log_time &now, const char **buf, bool reverse) {
258 const char *cp;
259 if ((cp = now.strptime(*buf, "[ %s.%q]"))) {
260 static const char suspend[] = "PM: suspend entry ";
261 static const char resume[] = "PM: suspend exit ";
262 static const char suspended[] = "Suspended for ";
263
264 if (isspace(*cp)) {
265 ++cp;
266 }
267 if (!strncmp(cp, suspend, sizeof(suspend) - 1)) {
268 calculateCorrection(now, cp + sizeof(suspend) - 1);
269 } else if (!strncmp(cp, resume, sizeof(resume) - 1)) {
270 calculateCorrection(now, cp + sizeof(resume) - 1);
271 } else if (!strncmp(cp, suspended, sizeof(suspended) - 1)) {
272 log_time real;
273 char *endp;
274 real.tv_sec = strtol(cp + sizeof(suspended) - 1, &endp, 10);
275 if (*endp == '.') {
276 real.tv_nsec = strtol(endp + 1, &endp, 10) * 1000000L;
277 if (reverse) {
278 correction -= real;
279 } else {
280 correction += real;
281 }
282 }
283 }
284
285 convertMonotonicToReal(now);
286 *buf = cp;
287 } else {
288 now = log_time(CLOCK_REALTIME);
289 }
290}
291
292// Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a
293// compensated start time.
294void LogKlog::synchronize(const char *buf) {
295 const char *cp = strstr(buf, "] PM: suspend e");
296 if (!cp) {
297 return;
298 }
299
300 do {
301 --cp;
302 } while ((cp > buf) && (isdigit(*cp) || isspace(*cp) || (*cp == '.')));
303
304 log_time now;
305 sniffTime(now, &cp, true);
306
307 char *suspended = strstr(buf, "] Suspended for ");
308 if (!suspended || (suspended > cp)) {
309 return;
310 }
311 cp = suspended;
312
313 do {
314 --cp;
315 } while ((cp > buf) && (isdigit(*cp) || isspace(*cp) || (*cp == '.')));
316
317 sniffTime(now, &cp, true);
318}
319
320// kernel log prefix, convert to a kernel log priority number
321static int parseKernelPrio(const char **buf) {
322 int pri = LOG_USER | LOG_INFO;
323 const char *cp = *buf;
324 if (*cp == '<') {
325 pri = 0;
326 while(isdigit(*++cp)) {
327 pri = (pri * 10) + *cp - '0';
328 }
329 if (*cp == '>') {
330 ++cp;
331 } else {
332 cp = *buf;
333 pri = LOG_USER | LOG_INFO;
334 }
335 *buf = cp;
336 }
337 return pri;
338}
339
340// Convert kernel log priority number into an Android Logger priority number
341static int convertKernelPrioToAndroidPrio(int pri) {
342 switch(pri & LOG_PRIMASK) {
343 case LOG_EMERG:
344 // FALLTHRU
345 case LOG_ALERT:
346 // FALLTHRU
347 case LOG_CRIT:
348 return ANDROID_LOG_FATAL;
349
350 case LOG_ERR:
351 return ANDROID_LOG_ERROR;
352
353 case LOG_WARNING:
354 return ANDROID_LOG_WARN;
355
356 default:
357 // FALLTHRU
358 case LOG_NOTICE:
359 // FALLTHRU
360 case LOG_INFO:
361 break;
362
363 case LOG_DEBUG:
364 return ANDROID_LOG_DEBUG;
365 }
366
367 return ANDROID_LOG_INFO;
368}
369
370//
371// log a message into the kernel log buffer
372//
373// Filter rules to parse <PRI> <TIME> <tag> and <message> in order for
374// them to appear correct in the logcat output:
375//
376// LOG_KERN (0):
377// <PRI>[<TIME>] <tag> ":" <message>
378// <PRI>[<TIME>] <tag> <tag> ":" <message>
379// <PRI>[<TIME>] <tag> <tag>_work ":" <message>
380// <PRI>[<TIME>] <tag> '<tag>.<num>' ":" <message>
381// <PRI>[<TIME>] <tag> '<tag><num>' ":" <message>
382// <PRI>[<TIME>] <tag>_host '<tag>.<num>' ":" <message>
383// (unimplemented) <PRI>[<TIME>] <tag> '<num>.<tag>' ":" <message>
384// <PRI>[<TIME>] "[INFO]"<tag> : <message>
385// <PRI>[<TIME>] "------------[ cut here ]------------" (?)
386// <PRI>[<TIME>] "---[ end trace 3225a3070ca3e4ac ]---" (?)
387// LOG_USER, LOG_MAIL, LOG_DAEMON, LOG_AUTH, LOG_SYSLOG, LOG_LPR, LOG_NEWS
388// LOG_UUCP, LOG_CRON, LOG_AUTHPRIV, LOG_FTP:
389// <PRI+TAG>[<TIME>] (see sys/syslog.h)
390// Observe:
391// Minimum tag length = 3 NB: drops things like r5:c00bbadf, but allow PM:
392// Maximum tag words = 2
393// Maximum tag length = 16 NB: we are thinking of how ugly logcat can get.
394// Not a Tag if there is no message content.
395// leading additional spaces means no tag, inherit last tag.
396// Not a Tag if <tag>: is "ERROR:", "WARNING:", "INFO:" or "CPU:"
397// Drop:
398// empty messages
399// messages with ' audit(' in them if auditd is running
400// logd.klogd:
401// return -1 if message logd.klogd: <signature>
402//
403int LogKlog::log(const char *buf) {
404 if (auditd && strstr(buf, " audit(")) {
405 return 0;
406 }
407
408 int pri = parseKernelPrio(&buf);
409
410 log_time now;
411 sniffTime(now, &buf, false);
412
413 // sniff for start marker
414 const char klogd_message[] = "logd.klogd: ";
415 if (!strncmp(buf, klogd_message, sizeof(klogd_message) - 1)) {
416 char *endp;
417 uint64_t sig = strtoll(buf + sizeof(klogd_message) - 1, &endp, 10);
418 if (sig == signature.nsec()) {
419 if (initialized) {
420 enableLogging = true;
421 } else {
422 enableLogging = false;
423 }
424 return -1;
425 }
426 return 0;
427 }
428
429 if (!enableLogging) {
430 return 0;
431 }
432
433 // Parse pid, tid and uid (not possible)
434 const pid_t pid = 0;
435 const pid_t tid = 0;
436 const uid_t uid = 0;
437
438 // Parse (rules at top) to pull out a tag from the incoming kernel message.
439 // Some may view the following as an ugly heuristic, the desire is to
440 // beautify the kernel logs into an Android Logging format; the goal is
441 // admirable but costly.
442 while (isspace(*buf)) {
443 ++buf;
444 }
445 if (!*buf) {
446 return 0;
447 }
448 const char *start = buf;
449 const char *tag = "";
450 const char *etag = tag;
451 if (!isspace(*buf)) {
452 const char *bt, *et, *cp;
453
454 bt = buf;
455 if (!strncmp(buf, "[INFO]", 6)) {
456 // <PRI>[<TIME>] "[INFO]"<tag> ":" message
457 bt = buf + 6;
458 }
459 for(et = bt; *et && (*et != ':') && !isspace(*et); ++et);
460 for(cp = et; isspace(*cp); ++cp);
461 size_t size;
462
463 if (*cp == ':') {
464 // One Word
465 tag = bt;
466 etag = et;
467 buf = cp + 1;
468 } else {
469 size = et - bt;
470 if (strncmp(bt, cp, size)) {
471 // <PRI>[<TIME>] <tag>_host '<tag>.<num>' : message
472 if (!strncmp(bt + size - 5, "_host", 5)
473 && !strncmp(bt, cp, size - 5)) {
474 const char *b = cp;
475 cp += size - 5;
476 if (*cp == '.') {
477 while (!isspace(*++cp) && (*cp != ':'));
478 const char *e;
479 for(e = cp; isspace(*cp); ++cp);
480 if (*cp == ':') {
481 tag = b;
482 etag = e;
483 buf = cp + 1;
484 }
485 }
486 } else {
487 while (!isspace(*++cp) && (*cp != ':'));
488 const char *e;
489 for(e = cp; isspace(*cp); ++cp);
490 // Two words
491 if (*cp == ':') {
492 tag = bt;
493 etag = e;
494 buf = cp + 1;
495 }
496 }
497 } else if (isspace(cp[size])) {
498 const char *b = cp;
499 cp += size;
500 while (isspace(*++cp));
501 // <PRI>[<TIME>] <tag> <tag> : message
502 if (*cp == ':') {
503 tag = bt;
504 etag = et;
505 buf = cp + 1;
506 }
507 } else if (cp[size] == ':') {
508 // <PRI>[<TIME>] <tag> <tag> : message
509 tag = bt;
510 etag = et;
511 buf = cp + size + 1;
512 } else if ((cp[size] == '.') || isdigit(cp[size])) {
513 // <PRI>[<TIME>] <tag> '<tag>.<num>' : message
514 // <PRI>[<TIME>] <tag> '<tag><num>' : message
515 const char *b = cp;
516 cp += size;
517 while (!isspace(*++cp) && (*cp != ':'));
518 const char *e = cp;
519 while (isspace(*cp)) {
520 ++cp;
521 }
522 if (*cp == ':') {
523 tag = b;
524 etag = e;
525 buf = cp + 1;
526 }
527 } else {
528 while (!isspace(*++cp) && (*cp != ':'));
529 const char *e = cp;
530 while (isspace(*cp)) {
531 ++cp;
532 }
533 // Two words
534 if (*cp == ':') {
535 tag = bt;
536 etag = e;
537 buf = cp + 1;
538 }
539 }
540 }
541 size = etag - tag;
542 if ((size <= 1)
543 || ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1])))
544 || ((size == 3) && !strncmp(tag, "CPU", 3))
545 || ((size == 7) && !strncmp(tag, "WARNING", 7))
546 || ((size == 5) && !strncmp(tag, "ERROR", 5))
547 || ((size == 4) && !strncmp(tag, "INFO", 4))) {
548 buf = start;
549 etag = tag = "";
550 }
551 }
552 size_t l = etag - tag;
553 while (isspace(*buf)) {
554 ++buf;
555 }
556 size_t n = 1 + l + 1 + strlen(buf) + 1;
557
558 // Allocate a buffer to hold the interpreted log message
559 int rc = n;
560 char *newstr = reinterpret_cast<char *>(malloc(n));
561 if (!newstr) {
562 rc = -ENOMEM;
563 return rc;
564 }
565 char *np = newstr;
566
567 // Convert priority into single-byte Android logger priority
568 *np = convertKernelPrioToAndroidPrio(pri);
569 ++np;
570
571 // Copy parsed tag following priority
572 strncpy(np, tag, l);
573 np += l;
574 *np = '\0';
575 ++np;
576
577 // Copy main message to the remainder
578 strcpy(np, buf);
579
580 // Log message
581 rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr,
582 (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX);
583 free(newstr);
584
585 // notify readers
586 if (!rc) {
587 reader->notifyNewLog();
588 }
589
590 return rc;
591}