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