blob: 0965ffd55bf81e1593f52bd31c32927db1a861ff [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 }
Mark Salyzyn618d0de2015-07-13 10:19:34 -070046 static const size_t max_prio_len = 4;
47 size_t len = 0;
Mark Salyzyn2c3b3002015-05-22 15:23:44 -070048 char c;
Mark Salyzyn618d0de2015-07-13 10:19:34 -070049 while (((c = *s++)) && (++len <= max_prio_len)) {
Mark Salyzynee49c6a2015-06-12 14:59:42 -070050 if (!isdigit(c)) {
Mark Salyzyn46d159d2015-08-26 09:30:00 -070051 return ((c == '>') && (*s == '[')) ? s : NULL;
Mark Salyzyn2c3b3002015-05-22 15:23:44 -070052 }
53 }
54 return NULL;
55}
56
57// called if we see a '[', s is the next character, returns pointer after ']'
58static char *is_timestamp(char *s) {
59 while (*s == ' ') {
60 ++s;
61 }
62 if (!isdigit(*s++)) {
63 return NULL;
64 }
65 bool first_period = true;
66 char c;
67 while ((c = *s++)) {
68 if ((c == '.') && first_period) {
69 first_period = false;
Mark Salyzynee49c6a2015-06-12 14:59:42 -070070 } else if (!isdigit(c)) {
71 return ((c == ']') && !first_period && (*s == ' ')) ? s : NULL;
Mark Salyzyn2c3b3002015-05-22 15:23:44 -070072 }
73 }
74 return NULL;
75}
76
77// Like strtok_r with "\r\n" except that we look for log signatures (regex)
Mark Salyzyn618d0de2015-07-13 10:19:34 -070078// \(\(<[0-9]\{1,4\}>\)\([[] *[0-9]+[.][0-9]+[]] \)\{0,1\}\|[[] *[0-9]+[.][0-9]+[]] \)
Mark Salyzyn2c3b3002015-05-22 15:23:44 -070079// and split if we see a second one without a newline.
80
81#define SIGNATURE_MASK 0xF0
82// <digit> following ('0' to '9' masked with ~SIGNATURE_MASK) added to signature
83#define LESS_THAN_SIG SIGNATURE_MASK
84#define OPEN_BRACKET_SIG ((SIGNATURE_MASK << 1) & SIGNATURE_MASK)
85// space is one more than <digit> of 9
Mark Salyzyn3e21de22015-06-08 14:51:30 -070086#define OPEN_BRACKET_SPACE ((char)(OPEN_BRACKET_SIG | 10))
Mark Salyzyn2c3b3002015-05-22 15:23:44 -070087
88char *log_strtok_r(char *s, char **last) {
89 if (!s) {
90 if (!(s = *last)) {
91 return NULL;
92 }
93 // fixup for log signature split <,
94 // LESS_THAN_SIG + <digit>
95 if ((*s & SIGNATURE_MASK) == LESS_THAN_SIG) {
96 *s = (*s & ~SIGNATURE_MASK) + '0';
97 *--s = '<';
98 }
99 // fixup for log signature split [,
100 // OPEN_BRACKET_SPACE is space, OPEN_BRACKET_SIG + <digit>
101 if ((*s & SIGNATURE_MASK) == OPEN_BRACKET_SIG) {
102 if (*s == OPEN_BRACKET_SPACE) {
103 *s = ' ';
104 } else {
105 *s = (*s & ~SIGNATURE_MASK) + '0';
106 }
107 *--s = '[';
108 }
109 }
110
111 s += strspn(s, "\r\n");
112
113 if (!*s) { // no non-delimiter characters
114 *last = NULL;
115 return NULL;
116 }
117 char *peek, *tok = s;
118
119 for (;;) {
120 char c = *s++;
121 switch (c) {
122 case '\0':
123 *last = NULL;
124 return tok;
125
126 case '\r':
127 case '\n':
128 s[-1] = '\0';
129 *last = s;
130 return tok;
131
132 case '<':
133 peek = is_prio(s);
134 if (!peek) {
135 break;
136 }
137 if (s != (tok + 1)) { // not first?
138 s[-1] = '\0';
139 *s &= ~SIGNATURE_MASK;
140 *s |= LESS_THAN_SIG; // signature for '<'
141 *last = s;
142 return tok;
143 }
144 s = peek;
145 if ((*s == '[') && ((peek = is_timestamp(s + 1)))) {
146 s = peek;
147 }
148 break;
149
150 case '[':
151 peek = is_timestamp(s);
152 if (!peek) {
153 break;
154 }
155 if (s != (tok + 1)) { // not first?
156 s[-1] = '\0';
157 if (*s == ' ') {
158 *s = OPEN_BRACKET_SPACE;
159 } else {
160 *s &= ~SIGNATURE_MASK;
161 *s |= OPEN_BRACKET_SIG; // signature for '['
162 }
163 *last = s;
164 return tok;
165 }
166 s = peek;
167 break;
168 }
169 }
Mark Salyzyn618d0de2015-07-13 10:19:34 -0700170 // NOTREACHED
Mark Salyzyn2c3b3002015-05-22 15:23:44 -0700171}
172
Mark Salyzynae4d9282014-10-15 08:49:39 -0700173log_time LogKlog::correction = log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC);
174
Mark Salyzyn77187782015-05-12 15:21:31 -0700175LogKlog::LogKlog(LogBuffer *buf, LogReader *reader, int fdWrite, int fdRead, bool auditd) :
176 SocketListener(fdRead, false),
177 logbuf(buf),
178 reader(reader),
179 signature(CLOCK_MONOTONIC),
Mark Salyzyn77187782015-05-12 15:21:31 -0700180 initialized(false),
181 enableLogging(true),
182 auditd(auditd) {
Mark Salyzynae4d9282014-10-15 08:49:39 -0700183 static const char klogd_message[] = "%slogd.klogd: %" PRIu64 "\n";
184 char buffer[sizeof(priority_message) + sizeof(klogd_message) + 20 - 4];
185 snprintf(buffer, sizeof(buffer), klogd_message, priority_message,
186 signature.nsec());
187 write(fdWrite, buffer, strlen(buffer));
188}
189
190bool LogKlog::onDataAvailable(SocketClient *cli) {
191 if (!initialized) {
192 prctl(PR_SET_NAME, "logd.klogd");
193 initialized = true;
194 enableLogging = false;
195 }
196
197 char buffer[LOGGER_ENTRY_MAX_PAYLOAD];
198 size_t len = 0;
199
200 for(;;) {
201 ssize_t retval = 0;
202 if ((sizeof(buffer) - 1 - len) > 0) {
203 retval = read(cli->getSocket(), buffer + len, sizeof(buffer) - 1 - len);
204 }
205 if ((retval == 0) && (len == 0)) {
206 break;
207 }
208 if (retval < 0) {
209 return false;
210 }
211 len += retval;
212 bool full = len == (sizeof(buffer) - 1);
213 char *ep = buffer + len;
214 *ep = '\0';
215 len = 0;
Mark Salyzyn2c3b3002015-05-22 15:23:44 -0700216 for(char *ptr = NULL, *tok = buffer;
217 ((tok = log_strtok_r(tok, &ptr)));
Mark Salyzynae4d9282014-10-15 08:49:39 -0700218 tok = NULL) {
219 if (((tok + strlen(tok)) == ep) && (retval != 0) && full) {
220 len = strlen(tok);
221 memmove(buffer, tok, len);
222 break;
223 }
224 if (*tok) {
225 log(tok);
226 }
227 }
228 }
229
230 return true;
231}
232
233
234void LogKlog::calculateCorrection(const log_time &monotonic,
235 const char *real_string) {
236 log_time real;
237 if (!real.strptime(real_string, "%Y-%m-%d %H:%M:%S.%09q UTC")) {
238 return;
239 }
240 // kernel report UTC, log_time::strptime is localtime from calendar.
241 // Bionic and liblog strptime does not support %z or %Z to pick up
242 // timezone so we are calculating our own correction.
243 time_t now = real.tv_sec;
244 struct tm tm;
245 memset(&tm, 0, sizeof(tm));
246 tm.tm_isdst = -1;
247 localtime_r(&now, &tm);
248 real.tv_sec += tm.tm_gmtoff;
249 correction = real - monotonic;
250}
251
Mark Salyzyn2d159bf2015-09-01 13:09:23 -0700252static const char suspendStr[] = "PM: suspend entry ";
253static const char resumeStr[] = "PM: suspend exit ";
254static const char suspendedStr[] = "Suspended for ";
255
Mark Salyzynae4d9282014-10-15 08:49:39 -0700256void LogKlog::sniffTime(log_time &now, const char **buf, bool reverse) {
257 const char *cp;
258 if ((cp = now.strptime(*buf, "[ %s.%q]"))) {
Mark Salyzyn2d159bf2015-09-01 13:09:23 -0700259 static const char healthd[] = "healthd";
260 static const char battery[] = ": battery ";
Mark Salyzynae4d9282014-10-15 08:49:39 -0700261
262 if (isspace(*cp)) {
263 ++cp;
264 }
Mark Salyzyn2d159bf2015-09-01 13:09:23 -0700265 *buf = cp;
266
267 const char *b;
268 if ((b = strstr(cp, suspendStr))) {
269 calculateCorrection(now, b + sizeof(suspendStr) - 1);
270 } else if ((b = strstr(cp, resumeStr))) {
271 calculateCorrection(now, b + sizeof(resumeStr) - 1);
272 } else if (((b = strstr(cp, healthd))) && ((b = strstr(b, battery)))) {
273 // NB: healthd is roughly 150us late, worth the price to deal with
274 // ntp-induced or hardware clock drift.
Mark Salyzynacb1ddf2015-07-23 09:22:50 -0700275 // look for " 2???-??-?? ??:??:??.????????? ???"
276 const char *tp;
Mark Salyzyn2d159bf2015-09-01 13:09:23 -0700277 for (tp = b + sizeof(battery) - 1; *tp && (*tp != '\n'); ++tp) {
Mark Salyzynacb1ddf2015-07-23 09:22:50 -0700278 if ((tp[0] == ' ') && (tp[1] == '2') && (tp[5] == '-')) {
279 calculateCorrection(now, tp + 1);
280 break;
281 }
282 }
Mark Salyzyn2d159bf2015-09-01 13:09:23 -0700283 } else if ((b = strstr(cp, suspendedStr))) {
Mark Salyzynae4d9282014-10-15 08:49:39 -0700284 log_time real;
285 char *endp;
Mark Salyzyn2d159bf2015-09-01 13:09:23 -0700286 real.tv_sec = strtol(b + sizeof(suspendedStr) - 1, &endp, 10);
Mark Salyzynae4d9282014-10-15 08:49:39 -0700287 if (*endp == '.') {
Mark Salyzyn2d159bf2015-09-01 13:09:23 -0700288 unsigned long multiplier = NS_PER_SEC;
289 real.tv_nsec = 0;
290 while (isdigit(*++endp) && (multiplier /= 10)) {
291 real.tv_nsec += (*endp - '0') * multiplier;
292 }
Mark Salyzynae4d9282014-10-15 08:49:39 -0700293 if (reverse) {
294 correction -= real;
295 } else {
296 correction += real;
297 }
298 }
299 }
300
301 convertMonotonicToReal(now);
Mark Salyzynae4d9282014-10-15 08:49:39 -0700302 } else {
303 now = log_time(CLOCK_REALTIME);
304 }
305}
306
Mark Salyzyn46d159d2015-08-26 09:30:00 -0700307pid_t LogKlog::sniffPid(const char *cp) {
308 while (*cp) {
309 // Mediatek kernels with modified printk
310 if (*cp == '[') {
311 int pid = 0;
312 char dummy;
313 if (sscanf(cp, "[%d:%*[a-z_./0-9:A-Z]]%c", &pid, &dummy) == 2) {
314 return pid;
315 }
316 break; // Only the first one
317 }
318 ++cp;
319 }
320 return 0;
321}
322
Mark Salyzynae4d9282014-10-15 08:49:39 -0700323// kernel log prefix, convert to a kernel log priority number
324static int parseKernelPrio(const char **buf) {
325 int pri = LOG_USER | LOG_INFO;
326 const char *cp = *buf;
327 if (*cp == '<') {
328 pri = 0;
329 while(isdigit(*++cp)) {
330 pri = (pri * 10) + *cp - '0';
331 }
332 if (*cp == '>') {
333 ++cp;
334 } else {
335 cp = *buf;
336 pri = LOG_USER | LOG_INFO;
337 }
338 *buf = cp;
339 }
340 return pri;
341}
342
Mark Salyzyn2d159bf2015-09-01 13:09:23 -0700343// Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a
344// compensated start time.
345void LogKlog::synchronize(const char *buf) {
346 const char *cp = strstr(buf, suspendStr);
347 if (!cp) {
348 cp = strstr(buf, resumeStr);
349 if (!cp) {
350 return;
351 }
352 } else {
353 const char *rp = strstr(buf, resumeStr);
354 if (rp && (rp < cp)) {
355 cp = rp;
356 }
357 }
358
359 do {
360 --cp;
361 } while ((cp > buf) && (*cp != '\n'));
362 if (*cp == '\n') {
363 ++cp;
364 }
365 parseKernelPrio(&cp);
366
367 log_time now;
368 sniffTime(now, &cp, true);
369
370 char *suspended = strstr(buf, suspendedStr);
371 if (!suspended || (suspended > cp)) {
372 return;
373 }
374 cp = suspended;
375
376 do {
377 --cp;
378 } while ((cp > buf) && (*cp != '\n'));
379 if (*cp == '\n') {
380 ++cp;
381 }
382 parseKernelPrio(&cp);
383
384 sniffTime(now, &cp, true);
385}
386
Mark Salyzynae4d9282014-10-15 08:49:39 -0700387// Convert kernel log priority number into an Android Logger priority number
388static int convertKernelPrioToAndroidPrio(int pri) {
389 switch(pri & LOG_PRIMASK) {
390 case LOG_EMERG:
391 // FALLTHRU
392 case LOG_ALERT:
393 // FALLTHRU
394 case LOG_CRIT:
395 return ANDROID_LOG_FATAL;
396
397 case LOG_ERR:
398 return ANDROID_LOG_ERROR;
399
400 case LOG_WARNING:
401 return ANDROID_LOG_WARN;
402
403 default:
404 // FALLTHRU
405 case LOG_NOTICE:
406 // FALLTHRU
407 case LOG_INFO:
408 break;
409
410 case LOG_DEBUG:
411 return ANDROID_LOG_DEBUG;
412 }
413
414 return ANDROID_LOG_INFO;
415}
416
Mark Salyzyn47dba712015-08-26 09:30:00 -0700417static const char *strnrchr(const char *s, size_t len, char c) {
418 const char *save = NULL;
419 for (;len; ++s, len--) {
420 if (*s == c) {
421 save = s;
422 }
423 }
424 return save;
425}
426
Mark Salyzynae4d9282014-10-15 08:49:39 -0700427//
428// log a message into the kernel log buffer
429//
430// Filter rules to parse <PRI> <TIME> <tag> and <message> in order for
431// them to appear correct in the logcat output:
432//
433// LOG_KERN (0):
434// <PRI>[<TIME>] <tag> ":" <message>
435// <PRI>[<TIME>] <tag> <tag> ":" <message>
436// <PRI>[<TIME>] <tag> <tag>_work ":" <message>
437// <PRI>[<TIME>] <tag> '<tag>.<num>' ":" <message>
438// <PRI>[<TIME>] <tag> '<tag><num>' ":" <message>
439// <PRI>[<TIME>] <tag>_host '<tag>.<num>' ":" <message>
440// (unimplemented) <PRI>[<TIME>] <tag> '<num>.<tag>' ":" <message>
441// <PRI>[<TIME>] "[INFO]"<tag> : <message>
442// <PRI>[<TIME>] "------------[ cut here ]------------" (?)
443// <PRI>[<TIME>] "---[ end trace 3225a3070ca3e4ac ]---" (?)
444// LOG_USER, LOG_MAIL, LOG_DAEMON, LOG_AUTH, LOG_SYSLOG, LOG_LPR, LOG_NEWS
445// LOG_UUCP, LOG_CRON, LOG_AUTHPRIV, LOG_FTP:
446// <PRI+TAG>[<TIME>] (see sys/syslog.h)
447// Observe:
448// Minimum tag length = 3 NB: drops things like r5:c00bbadf, but allow PM:
449// Maximum tag words = 2
450// Maximum tag length = 16 NB: we are thinking of how ugly logcat can get.
451// Not a Tag if there is no message content.
452// leading additional spaces means no tag, inherit last tag.
453// Not a Tag if <tag>: is "ERROR:", "WARNING:", "INFO:" or "CPU:"
454// Drop:
455// empty messages
456// messages with ' audit(' in them if auditd is running
457// logd.klogd:
458// return -1 if message logd.klogd: <signature>
459//
460int LogKlog::log(const char *buf) {
461 if (auditd && strstr(buf, " audit(")) {
462 return 0;
463 }
464
465 int pri = parseKernelPrio(&buf);
466
467 log_time now;
468 sniffTime(now, &buf, false);
469
470 // sniff for start marker
471 const char klogd_message[] = "logd.klogd: ";
Mark Salyzyn46d159d2015-08-26 09:30:00 -0700472 const char *start = strstr(buf, klogd_message);
473 if (start) {
474 uint64_t sig = strtoll(start + sizeof(klogd_message) - 1, NULL, 10);
Mark Salyzynae4d9282014-10-15 08:49:39 -0700475 if (sig == signature.nsec()) {
476 if (initialized) {
477 enableLogging = true;
478 } else {
479 enableLogging = false;
480 }
481 return -1;
482 }
483 return 0;
484 }
485
486 if (!enableLogging) {
487 return 0;
488 }
489
Mark Salyzyn46d159d2015-08-26 09:30:00 -0700490 // Parse pid, tid and uid
491 const pid_t pid = sniffPid(buf);
492 const pid_t tid = pid;
493 const uid_t uid = pid ? logbuf->pidToUid(pid) : 0;
Mark Salyzynae4d9282014-10-15 08:49:39 -0700494
495 // Parse (rules at top) to pull out a tag from the incoming kernel message.
496 // Some may view the following as an ugly heuristic, the desire is to
497 // beautify the kernel logs into an Android Logging format; the goal is
498 // admirable but costly.
499 while (isspace(*buf)) {
500 ++buf;
501 }
502 if (!*buf) {
503 return 0;
504 }
Mark Salyzyn46d159d2015-08-26 09:30:00 -0700505 start = buf;
Mark Salyzynae4d9282014-10-15 08:49:39 -0700506 const char *tag = "";
507 const char *etag = tag;
508 if (!isspace(*buf)) {
509 const char *bt, *et, *cp;
510
511 bt = buf;
512 if (!strncmp(buf, "[INFO]", 6)) {
513 // <PRI>[<TIME>] "[INFO]"<tag> ":" message
514 bt = buf + 6;
515 }
Mark Salyzyn46d159d2015-08-26 09:30:00 -0700516 for(et = bt; *et && (*et != ':') && !isspace(*et); ++et) {
517 // skip ':' within [ ... ]
518 if (*et == '[') {
519 while (*et && *et != ']') {
520 ++et;
521 }
522 }
523 }
Mark Salyzynae4d9282014-10-15 08:49:39 -0700524 for(cp = et; isspace(*cp); ++cp);
525 size_t size;
526
527 if (*cp == ':') {
528 // One Word
529 tag = bt;
530 etag = et;
531 buf = cp + 1;
532 } else {
533 size = et - bt;
534 if (strncmp(bt, cp, size)) {
535 // <PRI>[<TIME>] <tag>_host '<tag>.<num>' : message
536 if (!strncmp(bt + size - 5, "_host", 5)
Mark Salyzyn618d0de2015-07-13 10:19:34 -0700537 && !strncmp(bt, cp, size - 5)) {
Mark Salyzynae4d9282014-10-15 08:49:39 -0700538 const char *b = cp;
539 cp += size - 5;
540 if (*cp == '.') {
541 while (!isspace(*++cp) && (*cp != ':'));
542 const char *e;
543 for(e = cp; isspace(*cp); ++cp);
544 if (*cp == ':') {
545 tag = b;
546 etag = e;
547 buf = cp + 1;
548 }
549 }
550 } else {
551 while (!isspace(*++cp) && (*cp != ':'));
552 const char *e;
553 for(e = cp; isspace(*cp); ++cp);
554 // Two words
555 if (*cp == ':') {
556 tag = bt;
557 etag = e;
558 buf = cp + 1;
559 }
560 }
561 } else if (isspace(cp[size])) {
Mark Salyzynae4d9282014-10-15 08:49:39 -0700562 cp += size;
563 while (isspace(*++cp));
564 // <PRI>[<TIME>] <tag> <tag> : message
565 if (*cp == ':') {
566 tag = bt;
567 etag = et;
568 buf = cp + 1;
569 }
570 } else if (cp[size] == ':') {
571 // <PRI>[<TIME>] <tag> <tag> : message
572 tag = bt;
573 etag = et;
574 buf = cp + size + 1;
575 } else if ((cp[size] == '.') || isdigit(cp[size])) {
576 // <PRI>[<TIME>] <tag> '<tag>.<num>' : message
577 // <PRI>[<TIME>] <tag> '<tag><num>' : message
578 const char *b = cp;
579 cp += size;
580 while (!isspace(*++cp) && (*cp != ':'));
581 const char *e = cp;
582 while (isspace(*cp)) {
583 ++cp;
584 }
585 if (*cp == ':') {
586 tag = b;
587 etag = e;
588 buf = cp + 1;
589 }
590 } else {
591 while (!isspace(*++cp) && (*cp != ':'));
592 const char *e = cp;
593 while (isspace(*cp)) {
594 ++cp;
595 }
596 // Two words
597 if (*cp == ':') {
598 tag = bt;
599 etag = e;
600 buf = cp + 1;
601 }
602 }
603 }
604 size = etag - tag;
605 if ((size <= 1)
Mark Salyzyn618d0de2015-07-13 10:19:34 -0700606 // register names like x9
607 || ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1])))
608 // register names like x18 but not driver names like en0
609 || ((size == 3) && (isdigit(tag[1]) && isdigit(tag[2])))
610 // blacklist
611 || ((size == 3) && !strncmp(tag, "CPU", 3))
612 || ((size == 7) && !strncmp(tag, "WARNING", 7))
613 || ((size == 5) && !strncmp(tag, "ERROR", 5))
614 || ((size == 4) && !strncmp(tag, "INFO", 4))) {
Mark Salyzynae4d9282014-10-15 08:49:39 -0700615 buf = start;
616 etag = tag = "";
617 }
618 }
Mark Salyzyn46d159d2015-08-26 09:30:00 -0700619 // Suppress additional stutter in tag:
620 // eg: [143:healthd]healthd -> [143:healthd]
621 size_t taglen = etag - tag;
622 // Mediatek-special printk induced stutter
Mark Salyzyn47dba712015-08-26 09:30:00 -0700623 const char *mp = strnrchr(tag, ']', taglen);
624 if (mp && (++mp < etag)) {
625 size_t s = etag - mp;
626 if (((s + s) < taglen) && !memcmp(mp, mp - 1 - s, s)) {
627 taglen = mp - tag;
Mark Salyzyn46d159d2015-08-26 09:30:00 -0700628 }
629 }
Mark Salyzynee49c6a2015-06-12 14:59:42 -0700630 // skip leading space
Mark Salyzynae4d9282014-10-15 08:49:39 -0700631 while (isspace(*buf)) {
632 ++buf;
633 }
Mark Salyzynee49c6a2015-06-12 14:59:42 -0700634 // truncate trailing space
635 size_t b = strlen(buf);
636 while (b && isspace(buf[b-1])) {
637 --b;
638 }
639 // trick ... allow tag with empty content to be logged. log() drops empty
Mark Salyzyn46d159d2015-08-26 09:30:00 -0700640 if (!b && taglen) {
Mark Salyzynee49c6a2015-06-12 14:59:42 -0700641 buf = " ";
642 b = 1;
643 }
Mark Salyzyn46d159d2015-08-26 09:30:00 -0700644 size_t n = 1 + taglen + 1 + b + 1;
Mark Salyzyn47dba712015-08-26 09:30:00 -0700645 int rc = n;
646 if ((taglen > n) || (b > n)) { // Can not happen ...
647 rc = -EINVAL;
648 return rc;
649 }
Mark Salyzynae4d9282014-10-15 08:49:39 -0700650
651 // Allocate a buffer to hold the interpreted log message
Mark Salyzynae4d9282014-10-15 08:49:39 -0700652 char *newstr = reinterpret_cast<char *>(malloc(n));
653 if (!newstr) {
654 rc = -ENOMEM;
655 return rc;
656 }
Mark Salyzyn47dba712015-08-26 09:30:00 -0700657 char *np = newstr;
Mark Salyzynae4d9282014-10-15 08:49:39 -0700658
659 // Convert priority into single-byte Android logger priority
660 *np = convertKernelPrioToAndroidPrio(pri);
661 ++np;
662
663 // Copy parsed tag following priority
Mark Salyzyn46d159d2015-08-26 09:30:00 -0700664 strncpy(np, tag, taglen);
665 np += taglen;
Mark Salyzynae4d9282014-10-15 08:49:39 -0700666 *np = '\0';
667 ++np;
668
669 // Copy main message to the remainder
Mark Salyzynee49c6a2015-06-12 14:59:42 -0700670 strncpy(np, buf, b);
671 np[b] = '\0';
Mark Salyzynae4d9282014-10-15 08:49:39 -0700672
673 // Log message
674 rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr,
675 (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX);
676 free(newstr);
677
678 // notify readers
679 if (!rc) {
680 reader->notifyNewLog();
681 }
682
683 return rc;
684}