Mark Salyzyn | e73a18b | 2014-10-15 08:49:39 -0700 | [diff] [blame] | 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 <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 | |
| 37 | static const char priority_message[] = { KMSG_PRIORITY(LOG_INFO), '\0' }; |
| 38 | |
Mark Salyzyn | 4c72295 | 2015-05-22 15:23:44 -0700 | [diff] [blame] | 39 | // Parsing is hard |
| 40 | |
| 41 | // called if we see a '<', s is the next character, returns pointer after '>' |
| 42 | static char *is_prio(char *s) { |
| 43 | if (!isdigit(*s++)) { |
| 44 | return NULL; |
| 45 | } |
Mark Salyzyn | 7f791e6 | 2015-07-13 10:19:34 -0700 | [diff] [blame] | 46 | static const size_t max_prio_len = 4; |
| 47 | size_t len = 0; |
Mark Salyzyn | 4c72295 | 2015-05-22 15:23:44 -0700 | [diff] [blame] | 48 | char c; |
Mark Salyzyn | 7f791e6 | 2015-07-13 10:19:34 -0700 | [diff] [blame] | 49 | while (((c = *s++)) && (++len <= max_prio_len)) { |
Mark Salyzyn | 12c8efc | 2015-06-12 14:59:42 -0700 | [diff] [blame] | 50 | if (!isdigit(c)) { |
Mark Salyzyn | 6d4661c | 2015-08-26 09:30:00 -0700 | [diff] [blame] | 51 | return ((c == '>') && (*s == '[')) ? s : NULL; |
Mark Salyzyn | 4c72295 | 2015-05-22 15:23:44 -0700 | [diff] [blame] | 52 | } |
| 53 | } |
| 54 | return NULL; |
| 55 | } |
| 56 | |
| 57 | // called if we see a '[', s is the next character, returns pointer after ']' |
| 58 | static 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 Salyzyn | 12c8efc | 2015-06-12 14:59:42 -0700 | [diff] [blame] | 70 | } else if (!isdigit(c)) { |
| 71 | return ((c == ']') && !first_period && (*s == ' ')) ? s : NULL; |
Mark Salyzyn | 4c72295 | 2015-05-22 15:23:44 -0700 | [diff] [blame] | 72 | } |
| 73 | } |
| 74 | return NULL; |
| 75 | } |
| 76 | |
| 77 | // Like strtok_r with "\r\n" except that we look for log signatures (regex) |
Mark Salyzyn | 7f791e6 | 2015-07-13 10:19:34 -0700 | [diff] [blame] | 78 | // \(\(<[0-9]\{1,4\}>\)\([[] *[0-9]+[.][0-9]+[]] \)\{0,1\}\|[[] *[0-9]+[.][0-9]+[]] \) |
Mark Salyzyn | 4c72295 | 2015-05-22 15:23:44 -0700 | [diff] [blame] | 79 | // 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 Salyzyn | ba58a6d | 2015-06-08 14:51:30 -0700 | [diff] [blame] | 86 | #define OPEN_BRACKET_SPACE ((char)(OPEN_BRACKET_SIG | 10)) |
Mark Salyzyn | 4c72295 | 2015-05-22 15:23:44 -0700 | [diff] [blame] | 87 | |
| 88 | char *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 Salyzyn | 7f791e6 | 2015-07-13 10:19:34 -0700 | [diff] [blame] | 170 | // NOTREACHED |
Mark Salyzyn | 4c72295 | 2015-05-22 15:23:44 -0700 | [diff] [blame] | 171 | } |
| 172 | |
Mark Salyzyn | e73a18b | 2014-10-15 08:49:39 -0700 | [diff] [blame] | 173 | log_time LogKlog::correction = log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC); |
| 174 | |
Mark Salyzyn | cd766f9 | 2015-05-12 15:21:31 -0700 | [diff] [blame] | 175 | LogKlog::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 Salyzyn | cd766f9 | 2015-05-12 15:21:31 -0700 | [diff] [blame] | 180 | initialized(false), |
| 181 | enableLogging(true), |
| 182 | auditd(auditd) { |
Mark Salyzyn | e73a18b | 2014-10-15 08:49:39 -0700 | [diff] [blame] | 183 | 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 | |
| 190 | bool 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 Salyzyn | 4c72295 | 2015-05-22 15:23:44 -0700 | [diff] [blame] | 216 | for(char *ptr = NULL, *tok = buffer; |
| 217 | ((tok = log_strtok_r(tok, &ptr))); |
Mark Salyzyn | e73a18b | 2014-10-15 08:49:39 -0700 | [diff] [blame] | 218 | 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 | |
| 234 | void 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 | |
| 252 | void LogKlog::sniffTime(log_time &now, const char **buf, bool reverse) { |
| 253 | const char *cp; |
| 254 | if ((cp = now.strptime(*buf, "[ %s.%q]"))) { |
| 255 | static const char suspend[] = "PM: suspend entry "; |
| 256 | static const char resume[] = "PM: suspend exit "; |
Mark Salyzyn | 201b0b7 | 2015-07-23 09:22:50 -0700 | [diff] [blame] | 257 | static const char healthd[] = "healthd: battery "; |
Mark Salyzyn | e73a18b | 2014-10-15 08:49:39 -0700 | [diff] [blame] | 258 | static const char suspended[] = "Suspended for "; |
| 259 | |
| 260 | if (isspace(*cp)) { |
| 261 | ++cp; |
| 262 | } |
| 263 | if (!strncmp(cp, suspend, sizeof(suspend) - 1)) { |
| 264 | calculateCorrection(now, cp + sizeof(suspend) - 1); |
| 265 | } else if (!strncmp(cp, resume, sizeof(resume) - 1)) { |
| 266 | calculateCorrection(now, cp + sizeof(resume) - 1); |
Mark Salyzyn | 201b0b7 | 2015-07-23 09:22:50 -0700 | [diff] [blame] | 267 | } else if (!strncmp(cp, healthd, sizeof(healthd) - 1)) { |
| 268 | // look for " 2???-??-?? ??:??:??.????????? ???" |
| 269 | const char *tp; |
| 270 | for (tp = cp + sizeof(healthd) - 1; *tp && (*tp != '\n'); ++tp) { |
| 271 | if ((tp[0] == ' ') && (tp[1] == '2') && (tp[5] == '-')) { |
| 272 | calculateCorrection(now, tp + 1); |
| 273 | break; |
| 274 | } |
| 275 | } |
Mark Salyzyn | e73a18b | 2014-10-15 08:49:39 -0700 | [diff] [blame] | 276 | } else if (!strncmp(cp, suspended, sizeof(suspended) - 1)) { |
| 277 | log_time real; |
| 278 | char *endp; |
| 279 | real.tv_sec = strtol(cp + sizeof(suspended) - 1, &endp, 10); |
| 280 | if (*endp == '.') { |
| 281 | real.tv_nsec = strtol(endp + 1, &endp, 10) * 1000000L; |
| 282 | if (reverse) { |
| 283 | correction -= real; |
| 284 | } else { |
| 285 | correction += real; |
| 286 | } |
| 287 | } |
| 288 | } |
| 289 | |
| 290 | convertMonotonicToReal(now); |
| 291 | *buf = cp; |
| 292 | } else { |
| 293 | now = log_time(CLOCK_REALTIME); |
| 294 | } |
| 295 | } |
| 296 | |
Mark Salyzyn | 6d4661c | 2015-08-26 09:30:00 -0700 | [diff] [blame] | 297 | pid_t LogKlog::sniffPid(const char *cp) { |
| 298 | while (*cp) { |
| 299 | // Mediatek kernels with modified printk |
| 300 | if (*cp == '[') { |
| 301 | int pid = 0; |
| 302 | char dummy; |
| 303 | if (sscanf(cp, "[%d:%*[a-z_./0-9:A-Z]]%c", &pid, &dummy) == 2) { |
| 304 | return pid; |
| 305 | } |
| 306 | break; // Only the first one |
| 307 | } |
| 308 | ++cp; |
| 309 | } |
| 310 | return 0; |
| 311 | } |
| 312 | |
Mark Salyzyn | e73a18b | 2014-10-15 08:49:39 -0700 | [diff] [blame] | 313 | // Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a |
| 314 | // compensated start time. |
| 315 | void LogKlog::synchronize(const char *buf) { |
| 316 | const char *cp = strstr(buf, "] PM: suspend e"); |
| 317 | if (!cp) { |
| 318 | return; |
| 319 | } |
| 320 | |
| 321 | do { |
| 322 | --cp; |
| 323 | } while ((cp > buf) && (isdigit(*cp) || isspace(*cp) || (*cp == '.'))); |
| 324 | |
| 325 | log_time now; |
| 326 | sniffTime(now, &cp, true); |
| 327 | |
| 328 | char *suspended = strstr(buf, "] Suspended for "); |
| 329 | if (!suspended || (suspended > cp)) { |
| 330 | return; |
| 331 | } |
| 332 | cp = suspended; |
| 333 | |
| 334 | do { |
| 335 | --cp; |
| 336 | } while ((cp > buf) && (isdigit(*cp) || isspace(*cp) || (*cp == '.'))); |
| 337 | |
| 338 | sniffTime(now, &cp, true); |
| 339 | } |
| 340 | |
| 341 | // kernel log prefix, convert to a kernel log priority number |
| 342 | static int parseKernelPrio(const char **buf) { |
| 343 | int pri = LOG_USER | LOG_INFO; |
| 344 | const char *cp = *buf; |
| 345 | if (*cp == '<') { |
| 346 | pri = 0; |
| 347 | while(isdigit(*++cp)) { |
| 348 | pri = (pri * 10) + *cp - '0'; |
| 349 | } |
| 350 | if (*cp == '>') { |
| 351 | ++cp; |
| 352 | } else { |
| 353 | cp = *buf; |
| 354 | pri = LOG_USER | LOG_INFO; |
| 355 | } |
| 356 | *buf = cp; |
| 357 | } |
| 358 | return pri; |
| 359 | } |
| 360 | |
| 361 | // Convert kernel log priority number into an Android Logger priority number |
| 362 | static int convertKernelPrioToAndroidPrio(int pri) { |
| 363 | switch(pri & LOG_PRIMASK) { |
| 364 | case LOG_EMERG: |
| 365 | // FALLTHRU |
| 366 | case LOG_ALERT: |
| 367 | // FALLTHRU |
| 368 | case LOG_CRIT: |
| 369 | return ANDROID_LOG_FATAL; |
| 370 | |
| 371 | case LOG_ERR: |
| 372 | return ANDROID_LOG_ERROR; |
| 373 | |
| 374 | case LOG_WARNING: |
| 375 | return ANDROID_LOG_WARN; |
| 376 | |
| 377 | default: |
| 378 | // FALLTHRU |
| 379 | case LOG_NOTICE: |
| 380 | // FALLTHRU |
| 381 | case LOG_INFO: |
| 382 | break; |
| 383 | |
| 384 | case LOG_DEBUG: |
| 385 | return ANDROID_LOG_DEBUG; |
| 386 | } |
| 387 | |
| 388 | return ANDROID_LOG_INFO; |
| 389 | } |
| 390 | |
Mark Salyzyn | 98295bb | 2015-08-26 09:30:00 -0700 | [diff] [blame^] | 391 | static const char *strnrchr(const char *s, size_t len, char c) { |
| 392 | const char *save = NULL; |
| 393 | for (;len; ++s, len--) { |
| 394 | if (*s == c) { |
| 395 | save = s; |
| 396 | } |
| 397 | } |
| 398 | return save; |
| 399 | } |
| 400 | |
Mark Salyzyn | e73a18b | 2014-10-15 08:49:39 -0700 | [diff] [blame] | 401 | // |
| 402 | // log a message into the kernel log buffer |
| 403 | // |
| 404 | // Filter rules to parse <PRI> <TIME> <tag> and <message> in order for |
| 405 | // them to appear correct in the logcat output: |
| 406 | // |
| 407 | // LOG_KERN (0): |
| 408 | // <PRI>[<TIME>] <tag> ":" <message> |
| 409 | // <PRI>[<TIME>] <tag> <tag> ":" <message> |
| 410 | // <PRI>[<TIME>] <tag> <tag>_work ":" <message> |
| 411 | // <PRI>[<TIME>] <tag> '<tag>.<num>' ":" <message> |
| 412 | // <PRI>[<TIME>] <tag> '<tag><num>' ":" <message> |
| 413 | // <PRI>[<TIME>] <tag>_host '<tag>.<num>' ":" <message> |
| 414 | // (unimplemented) <PRI>[<TIME>] <tag> '<num>.<tag>' ":" <message> |
| 415 | // <PRI>[<TIME>] "[INFO]"<tag> : <message> |
| 416 | // <PRI>[<TIME>] "------------[ cut here ]------------" (?) |
| 417 | // <PRI>[<TIME>] "---[ end trace 3225a3070ca3e4ac ]---" (?) |
| 418 | // LOG_USER, LOG_MAIL, LOG_DAEMON, LOG_AUTH, LOG_SYSLOG, LOG_LPR, LOG_NEWS |
| 419 | // LOG_UUCP, LOG_CRON, LOG_AUTHPRIV, LOG_FTP: |
| 420 | // <PRI+TAG>[<TIME>] (see sys/syslog.h) |
| 421 | // Observe: |
| 422 | // Minimum tag length = 3 NB: drops things like r5:c00bbadf, but allow PM: |
| 423 | // Maximum tag words = 2 |
| 424 | // Maximum tag length = 16 NB: we are thinking of how ugly logcat can get. |
| 425 | // Not a Tag if there is no message content. |
| 426 | // leading additional spaces means no tag, inherit last tag. |
| 427 | // Not a Tag if <tag>: is "ERROR:", "WARNING:", "INFO:" or "CPU:" |
| 428 | // Drop: |
| 429 | // empty messages |
| 430 | // messages with ' audit(' in them if auditd is running |
| 431 | // logd.klogd: |
| 432 | // return -1 if message logd.klogd: <signature> |
| 433 | // |
| 434 | int LogKlog::log(const char *buf) { |
| 435 | if (auditd && strstr(buf, " audit(")) { |
| 436 | return 0; |
| 437 | } |
| 438 | |
| 439 | int pri = parseKernelPrio(&buf); |
| 440 | |
| 441 | log_time now; |
| 442 | sniffTime(now, &buf, false); |
| 443 | |
| 444 | // sniff for start marker |
| 445 | const char klogd_message[] = "logd.klogd: "; |
Mark Salyzyn | 6d4661c | 2015-08-26 09:30:00 -0700 | [diff] [blame] | 446 | const char *start = strstr(buf, klogd_message); |
| 447 | if (start) { |
| 448 | uint64_t sig = strtoll(start + sizeof(klogd_message) - 1, NULL, 10); |
Mark Salyzyn | e73a18b | 2014-10-15 08:49:39 -0700 | [diff] [blame] | 449 | if (sig == signature.nsec()) { |
| 450 | if (initialized) { |
| 451 | enableLogging = true; |
| 452 | } else { |
| 453 | enableLogging = false; |
| 454 | } |
| 455 | return -1; |
| 456 | } |
| 457 | return 0; |
| 458 | } |
| 459 | |
| 460 | if (!enableLogging) { |
| 461 | return 0; |
| 462 | } |
| 463 | |
Mark Salyzyn | 6d4661c | 2015-08-26 09:30:00 -0700 | [diff] [blame] | 464 | // Parse pid, tid and uid |
| 465 | const pid_t pid = sniffPid(buf); |
| 466 | const pid_t tid = pid; |
| 467 | const uid_t uid = pid ? logbuf->pidToUid(pid) : 0; |
Mark Salyzyn | e73a18b | 2014-10-15 08:49:39 -0700 | [diff] [blame] | 468 | |
| 469 | // Parse (rules at top) to pull out a tag from the incoming kernel message. |
| 470 | // Some may view the following as an ugly heuristic, the desire is to |
| 471 | // beautify the kernel logs into an Android Logging format; the goal is |
| 472 | // admirable but costly. |
| 473 | while (isspace(*buf)) { |
| 474 | ++buf; |
| 475 | } |
| 476 | if (!*buf) { |
| 477 | return 0; |
| 478 | } |
Mark Salyzyn | 6d4661c | 2015-08-26 09:30:00 -0700 | [diff] [blame] | 479 | start = buf; |
Mark Salyzyn | e73a18b | 2014-10-15 08:49:39 -0700 | [diff] [blame] | 480 | const char *tag = ""; |
| 481 | const char *etag = tag; |
| 482 | if (!isspace(*buf)) { |
| 483 | const char *bt, *et, *cp; |
| 484 | |
| 485 | bt = buf; |
| 486 | if (!strncmp(buf, "[INFO]", 6)) { |
| 487 | // <PRI>[<TIME>] "[INFO]"<tag> ":" message |
| 488 | bt = buf + 6; |
| 489 | } |
Mark Salyzyn | 6d4661c | 2015-08-26 09:30:00 -0700 | [diff] [blame] | 490 | for(et = bt; *et && (*et != ':') && !isspace(*et); ++et) { |
| 491 | // skip ':' within [ ... ] |
| 492 | if (*et == '[') { |
| 493 | while (*et && *et != ']') { |
| 494 | ++et; |
| 495 | } |
| 496 | } |
| 497 | } |
Mark Salyzyn | e73a18b | 2014-10-15 08:49:39 -0700 | [diff] [blame] | 498 | for(cp = et; isspace(*cp); ++cp); |
| 499 | size_t size; |
| 500 | |
| 501 | if (*cp == ':') { |
| 502 | // One Word |
| 503 | tag = bt; |
| 504 | etag = et; |
| 505 | buf = cp + 1; |
| 506 | } else { |
| 507 | size = et - bt; |
| 508 | if (strncmp(bt, cp, size)) { |
| 509 | // <PRI>[<TIME>] <tag>_host '<tag>.<num>' : message |
| 510 | if (!strncmp(bt + size - 5, "_host", 5) |
Mark Salyzyn | 7f791e6 | 2015-07-13 10:19:34 -0700 | [diff] [blame] | 511 | && !strncmp(bt, cp, size - 5)) { |
Mark Salyzyn | e73a18b | 2014-10-15 08:49:39 -0700 | [diff] [blame] | 512 | const char *b = cp; |
| 513 | cp += size - 5; |
| 514 | if (*cp == '.') { |
| 515 | while (!isspace(*++cp) && (*cp != ':')); |
| 516 | const char *e; |
| 517 | for(e = cp; isspace(*cp); ++cp); |
| 518 | if (*cp == ':') { |
| 519 | tag = b; |
| 520 | etag = e; |
| 521 | buf = cp + 1; |
| 522 | } |
| 523 | } |
| 524 | } else { |
| 525 | while (!isspace(*++cp) && (*cp != ':')); |
| 526 | const char *e; |
| 527 | for(e = cp; isspace(*cp); ++cp); |
| 528 | // Two words |
| 529 | if (*cp == ':') { |
| 530 | tag = bt; |
| 531 | etag = e; |
| 532 | buf = cp + 1; |
| 533 | } |
| 534 | } |
| 535 | } else if (isspace(cp[size])) { |
Mark Salyzyn | e73a18b | 2014-10-15 08:49:39 -0700 | [diff] [blame] | 536 | cp += size; |
| 537 | while (isspace(*++cp)); |
| 538 | // <PRI>[<TIME>] <tag> <tag> : message |
| 539 | if (*cp == ':') { |
| 540 | tag = bt; |
| 541 | etag = et; |
| 542 | buf = cp + 1; |
| 543 | } |
| 544 | } else if (cp[size] == ':') { |
| 545 | // <PRI>[<TIME>] <tag> <tag> : message |
| 546 | tag = bt; |
| 547 | etag = et; |
| 548 | buf = cp + size + 1; |
| 549 | } else if ((cp[size] == '.') || isdigit(cp[size])) { |
| 550 | // <PRI>[<TIME>] <tag> '<tag>.<num>' : message |
| 551 | // <PRI>[<TIME>] <tag> '<tag><num>' : message |
| 552 | const char *b = cp; |
| 553 | cp += size; |
| 554 | while (!isspace(*++cp) && (*cp != ':')); |
| 555 | const char *e = cp; |
| 556 | while (isspace(*cp)) { |
| 557 | ++cp; |
| 558 | } |
| 559 | if (*cp == ':') { |
| 560 | tag = b; |
| 561 | etag = e; |
| 562 | buf = cp + 1; |
| 563 | } |
| 564 | } else { |
| 565 | while (!isspace(*++cp) && (*cp != ':')); |
| 566 | const char *e = cp; |
| 567 | while (isspace(*cp)) { |
| 568 | ++cp; |
| 569 | } |
| 570 | // Two words |
| 571 | if (*cp == ':') { |
| 572 | tag = bt; |
| 573 | etag = e; |
| 574 | buf = cp + 1; |
| 575 | } |
| 576 | } |
| 577 | } |
| 578 | size = etag - tag; |
| 579 | if ((size <= 1) |
Mark Salyzyn | 7f791e6 | 2015-07-13 10:19:34 -0700 | [diff] [blame] | 580 | // register names like x9 |
| 581 | || ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1]))) |
| 582 | // register names like x18 but not driver names like en0 |
| 583 | || ((size == 3) && (isdigit(tag[1]) && isdigit(tag[2]))) |
| 584 | // blacklist |
| 585 | || ((size == 3) && !strncmp(tag, "CPU", 3)) |
| 586 | || ((size == 7) && !strncmp(tag, "WARNING", 7)) |
| 587 | || ((size == 5) && !strncmp(tag, "ERROR", 5)) |
| 588 | || ((size == 4) && !strncmp(tag, "INFO", 4))) { |
Mark Salyzyn | e73a18b | 2014-10-15 08:49:39 -0700 | [diff] [blame] | 589 | buf = start; |
| 590 | etag = tag = ""; |
| 591 | } |
| 592 | } |
Mark Salyzyn | 6d4661c | 2015-08-26 09:30:00 -0700 | [diff] [blame] | 593 | // Suppress additional stutter in tag: |
| 594 | // eg: [143:healthd]healthd -> [143:healthd] |
| 595 | size_t taglen = etag - tag; |
| 596 | // Mediatek-special printk induced stutter |
Mark Salyzyn | 98295bb | 2015-08-26 09:30:00 -0700 | [diff] [blame^] | 597 | const char *mp = strnrchr(tag, ']', taglen); |
| 598 | if (mp && (++mp < etag)) { |
| 599 | size_t s = etag - mp; |
| 600 | if (((s + s) < taglen) && !memcmp(mp, mp - 1 - s, s)) { |
| 601 | taglen = mp - tag; |
Mark Salyzyn | 6d4661c | 2015-08-26 09:30:00 -0700 | [diff] [blame] | 602 | } |
| 603 | } |
Mark Salyzyn | 12c8efc | 2015-06-12 14:59:42 -0700 | [diff] [blame] | 604 | // skip leading space |
Mark Salyzyn | e73a18b | 2014-10-15 08:49:39 -0700 | [diff] [blame] | 605 | while (isspace(*buf)) { |
| 606 | ++buf; |
| 607 | } |
Mark Salyzyn | 12c8efc | 2015-06-12 14:59:42 -0700 | [diff] [blame] | 608 | // truncate trailing space |
| 609 | size_t b = strlen(buf); |
| 610 | while (b && isspace(buf[b-1])) { |
| 611 | --b; |
| 612 | } |
| 613 | // trick ... allow tag with empty content to be logged. log() drops empty |
Mark Salyzyn | 6d4661c | 2015-08-26 09:30:00 -0700 | [diff] [blame] | 614 | if (!b && taglen) { |
Mark Salyzyn | 12c8efc | 2015-06-12 14:59:42 -0700 | [diff] [blame] | 615 | buf = " "; |
| 616 | b = 1; |
| 617 | } |
Mark Salyzyn | 6d4661c | 2015-08-26 09:30:00 -0700 | [diff] [blame] | 618 | size_t n = 1 + taglen + 1 + b + 1; |
Mark Salyzyn | 98295bb | 2015-08-26 09:30:00 -0700 | [diff] [blame^] | 619 | int rc = n; |
| 620 | if ((taglen > n) || (b > n)) { // Can not happen ... |
| 621 | rc = -EINVAL; |
| 622 | return rc; |
| 623 | } |
Mark Salyzyn | e73a18b | 2014-10-15 08:49:39 -0700 | [diff] [blame] | 624 | |
| 625 | // Allocate a buffer to hold the interpreted log message |
Mark Salyzyn | e73a18b | 2014-10-15 08:49:39 -0700 | [diff] [blame] | 626 | char *newstr = reinterpret_cast<char *>(malloc(n)); |
| 627 | if (!newstr) { |
| 628 | rc = -ENOMEM; |
| 629 | return rc; |
| 630 | } |
Mark Salyzyn | 98295bb | 2015-08-26 09:30:00 -0700 | [diff] [blame^] | 631 | char *np = newstr; |
Mark Salyzyn | e73a18b | 2014-10-15 08:49:39 -0700 | [diff] [blame] | 632 | |
| 633 | // Convert priority into single-byte Android logger priority |
| 634 | *np = convertKernelPrioToAndroidPrio(pri); |
| 635 | ++np; |
| 636 | |
| 637 | // Copy parsed tag following priority |
Mark Salyzyn | 6d4661c | 2015-08-26 09:30:00 -0700 | [diff] [blame] | 638 | strncpy(np, tag, taglen); |
| 639 | np += taglen; |
Mark Salyzyn | e73a18b | 2014-10-15 08:49:39 -0700 | [diff] [blame] | 640 | *np = '\0'; |
| 641 | ++np; |
| 642 | |
| 643 | // Copy main message to the remainder |
Mark Salyzyn | 12c8efc | 2015-06-12 14:59:42 -0700 | [diff] [blame] | 644 | strncpy(np, buf, b); |
| 645 | np[b] = '\0'; |
Mark Salyzyn | e73a18b | 2014-10-15 08:49:39 -0700 | [diff] [blame] | 646 | |
| 647 | // Log message |
| 648 | rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr, |
| 649 | (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX); |
| 650 | free(newstr); |
| 651 | |
| 652 | // notify readers |
| 653 | if (!rc) { |
| 654 | reader->notifyNewLog(); |
| 655 | } |
| 656 | |
| 657 | return rc; |
| 658 | } |