1 /* 2 * Copyright (C) 2007-2016 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 <fcntl.h> 20 #include <stdbool.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <sys/types.h> 24 25 #include <private/android_filesystem_config.h> 26 #include <private/android_logger.h> 27 28 #include "config_read.h" 29 #include "logger.h" 30 31 static int pmsgAvailable(log_id_t logId); 32 static int pmsgVersion(struct android_log_logger *logger, 33 struct android_log_transport_context *transp); 34 static int pmsgRead(struct android_log_logger_list *logger_list, 35 struct android_log_transport_context *transp, 36 struct log_msg *log_msg); 37 static void pmsgClose(struct android_log_logger_list *logger_list, 38 struct android_log_transport_context *transp); 39 static int pmsgClear(struct android_log_logger *logger, 40 struct android_log_transport_context *transp); 41 42 LIBLOG_HIDDEN struct android_log_transport_read pmsgLoggerRead = { 43 .node = { &pmsgLoggerRead.node, &pmsgLoggerRead.node }, 44 .name = "pmsg", 45 .available = pmsgAvailable, 46 .version = pmsgVersion, 47 .read = pmsgRead, 48 .poll = NULL, 49 .close = pmsgClose, 50 .clear = pmsgClear, 51 .setSize = NULL, 52 .getSize = NULL, 53 .getReadableSize = NULL, 54 .getPrune = NULL, 55 .setPrune = NULL, 56 .getStats = NULL, 57 }; 58 59 static int pmsgAvailable(log_id_t logId) 60 { 61 if (logId > LOG_ID_SECURITY) { 62 return -EINVAL; 63 } 64 if (access("/dev/pmsg0", W_OK) == 0) { 65 return 0; 66 } 67 return -EBADF; 68 } 69 70 /* Determine the credentials of the caller */ 71 static bool uid_has_log_permission(uid_t uid) 72 { 73 return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT); 74 } 75 76 static uid_t get_best_effective_uid() 77 { 78 uid_t euid; 79 uid_t uid; 80 gid_t gid; 81 ssize_t i; 82 static uid_t last_uid = (uid_t) -1; 83 84 if (last_uid != (uid_t) -1) { 85 return last_uid; 86 } 87 uid = __android_log_uid(); 88 if (uid_has_log_permission(uid)) { 89 return last_uid = uid; 90 } 91 euid = geteuid(); 92 if (uid_has_log_permission(euid)) { 93 return last_uid = euid; 94 } 95 gid = getgid(); 96 if (uid_has_log_permission(gid)) { 97 return last_uid = gid; 98 } 99 gid = getegid(); 100 if (uid_has_log_permission(gid)) { 101 return last_uid = gid; 102 } 103 i = getgroups((size_t) 0, NULL); 104 if (i > 0) { 105 gid_t list[i]; 106 107 getgroups(i, list); 108 while (--i >= 0) { 109 if (uid_has_log_permission(list[i])) { 110 return last_uid = list[i]; 111 } 112 } 113 } 114 return last_uid = uid; 115 } 116 117 static int pmsgClear(struct android_log_logger *logger __unused, 118 struct android_log_transport_context *transp __unused) 119 { 120 if (uid_has_log_permission(get_best_effective_uid())) { 121 return unlink("/sys/fs/pstore/pmsg-ramoops-0"); 122 } 123 errno = EPERM; 124 return -1; 125 } 126 127 /* 128 * returns the logger version 129 */ 130 static int pmsgVersion(struct android_log_logger *logger __unused, 131 struct android_log_transport_context *transp __unused) 132 { 133 return 4; 134 } 135 136 static int pmsgRead(struct android_log_logger_list *logger_list, 137 struct android_log_transport_context *transp, 138 struct log_msg *log_msg) 139 { 140 ssize_t ret; 141 off_t current, next; 142 uid_t uid; 143 struct android_log_logger *logger; 144 struct __attribute__((__packed__)) { 145 android_pmsg_log_header_t p; 146 android_log_header_t l; 147 } buf; 148 static uint8_t preread_count; 149 bool is_system; 150 151 memset(log_msg, 0, sizeof(*log_msg)); 152 153 if (transp->context.fd <= 0) { 154 int fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY | O_CLOEXEC); 155 156 if (fd < 0) { 157 return -errno; 158 } 159 if (fd == 0) { /* Argggg */ 160 fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY | O_CLOEXEC); 161 close(0); 162 if (fd < 0) { 163 return -errno; 164 } 165 } 166 transp->context.fd = fd; 167 preread_count = 0; 168 } 169 170 while(1) { 171 if (preread_count < sizeof(buf)) { 172 ret = TEMP_FAILURE_RETRY(read(transp->context.fd, 173 &buf.p.magic + preread_count, 174 sizeof(buf) - preread_count)); 175 if (ret < 0) { 176 return -errno; 177 } 178 preread_count += ret; 179 } 180 if (preread_count != sizeof(buf)) { 181 return preread_count ? -EIO : -EAGAIN; 182 } 183 if ((buf.p.magic != LOGGER_MAGIC) 184 || (buf.p.len <= sizeof(buf)) 185 || (buf.p.len > (sizeof(buf) + LOGGER_ENTRY_MAX_PAYLOAD)) 186 || (buf.l.id >= LOG_ID_MAX) 187 || (buf.l.realtime.tv_nsec >= NS_PER_SEC)) { 188 do { 189 memmove(&buf.p.magic, &buf.p.magic + 1, --preread_count); 190 } while (preread_count && (buf.p.magic != LOGGER_MAGIC)); 191 continue; 192 } 193 preread_count = 0; 194 195 if ((transp->logMask & (1 << buf.l.id)) && 196 ((!logger_list->start.tv_sec && !logger_list->start.tv_nsec) || 197 ((logger_list->start.tv_sec <= buf.l.realtime.tv_sec) && 198 ((logger_list->start.tv_sec != buf.l.realtime.tv_sec) || 199 (logger_list->start.tv_nsec <= 200 buf.l.realtime.tv_nsec)))) && 201 (!logger_list->pid || (logger_list->pid == buf.p.pid))) { 202 uid = get_best_effective_uid(); 203 is_system = uid_has_log_permission(uid); 204 if (is_system || (uid == buf.p.uid)) { 205 ret = TEMP_FAILURE_RETRY(read(transp->context.fd, 206 is_system ? 207 log_msg->entry_v4.msg : 208 log_msg->entry_v3.msg, 209 buf.p.len - sizeof(buf))); 210 if (ret < 0) { 211 return -errno; 212 } 213 if (ret != (ssize_t)(buf.p.len - sizeof(buf))) { 214 return -EIO; 215 } 216 217 log_msg->entry_v4.len = buf.p.len - sizeof(buf); 218 log_msg->entry_v4.hdr_size = is_system ? 219 sizeof(log_msg->entry_v4) : 220 sizeof(log_msg->entry_v3); 221 log_msg->entry_v4.pid = buf.p.pid; 222 log_msg->entry_v4.tid = buf.l.tid; 223 log_msg->entry_v4.sec = buf.l.realtime.tv_sec; 224 log_msg->entry_v4.nsec = buf.l.realtime.tv_nsec; 225 log_msg->entry_v4.lid = buf.l.id; 226 if (is_system) { 227 log_msg->entry_v4.uid = buf.p.uid; 228 } 229 230 return ret + log_msg->entry_v4.hdr_size; 231 } 232 } 233 234 current = TEMP_FAILURE_RETRY(lseek(transp->context.fd, 235 (off_t)0, SEEK_CUR)); 236 if (current < 0) { 237 return -errno; 238 } 239 next = TEMP_FAILURE_RETRY(lseek(transp->context.fd, 240 (off_t)(buf.p.len - sizeof(buf)), 241 SEEK_CUR)); 242 if (next < 0) { 243 return -errno; 244 } 245 if ((next - current) != (ssize_t)(buf.p.len - sizeof(buf))) { 246 return -EIO; 247 } 248 } 249 } 250 251 static void pmsgClose(struct android_log_logger_list *logger_list __unused, 252 struct android_log_transport_context *transp) { 253 if (transp->context.fd > 0) { 254 close (transp->context.fd); 255 } 256 transp->context.fd = 0; 257 } 258 259 LIBLOG_ABI_PRIVATE ssize_t __android_log_pmsg_file_read( 260 log_id_t logId, 261 char prio, 262 const char *prefix, 263 __android_log_pmsg_file_read_fn fn, void *arg) { 264 ssize_t ret; 265 struct android_log_logger_list logger_list; 266 struct android_log_transport_context transp; 267 struct content { 268 struct listnode node; 269 union { 270 struct logger_entry_v4 entry; 271 struct logger_entry_v4 entry_v4; 272 struct logger_entry_v3 entry_v3; 273 struct logger_entry_v2 entry_v2; 274 struct logger_entry entry_v1; 275 }; 276 } *content; 277 struct names { 278 struct listnode node; 279 struct listnode content; 280 log_id_t id; 281 char prio; 282 char name[]; 283 } *names; 284 struct listnode name_list; 285 struct listnode *node, *n; 286 size_t len, prefix_len; 287 288 if (!fn) { 289 return -EINVAL; 290 } 291 292 /* Add just enough clues in logger_list and transp to make API function */ 293 memset(&logger_list, 0, sizeof(logger_list)); 294 memset(&transp, 0, sizeof(transp)); 295 296 logger_list.mode = ANDROID_LOG_PSTORE | 297 ANDROID_LOG_NONBLOCK | 298 ANDROID_LOG_RDONLY; 299 transp.logMask = (unsigned)-1; 300 if (logId != LOG_ID_ANY) { 301 transp.logMask = (1 << logId); 302 } 303 transp.logMask &= ~((1 << LOG_ID_KERNEL) | 304 (1 << LOG_ID_EVENTS) | 305 (1 << LOG_ID_SECURITY)); 306 if (!transp.logMask) { 307 return -EINVAL; 308 } 309 310 /* Initialize name list */ 311 list_init(&name_list); 312 313 ret = SSIZE_MAX; 314 315 /* Validate incoming prefix, shift until it contains only 0 or 1 : or / */ 316 prefix_len = 0; 317 if (prefix) { 318 const char *prev = NULL, *last = NULL, *cp = prefix; 319 while ((cp = strpbrk(cp, "/:"))) { 320 prev = last; 321 last = cp; 322 cp = cp + 1; 323 } 324 if (prev) { 325 prefix = prev + 1; 326 } 327 prefix_len = strlen(prefix); 328 } 329 330 /* Read the file content */ 331 while (pmsgRead(&logger_list, &transp, &transp.logMsg) > 0) { 332 char *cp; 333 size_t hdr_size = transp.logMsg.entry.hdr_size ? 334 transp.logMsg.entry.hdr_size : sizeof(transp.logMsg.entry_v1); 335 char *msg = (char *)&transp.logMsg + hdr_size; 336 char *split = NULL; 337 338 /* Check for invalid sequence number */ 339 if ((transp.logMsg.entry.nsec % ANDROID_LOG_PMSG_FILE_SEQUENCE) || 340 ((transp.logMsg.entry.nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >= 341 ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE)) { 342 continue; 343 } 344 345 /* Determine if it has <dirbase>:<filebase> format for tag */ 346 len = transp.logMsg.entry.len - sizeof(prio); 347 for (cp = msg + sizeof(prio); 348 *cp && isprint(*cp) && !isspace(*cp) && --len; 349 ++cp) { 350 if (*cp == ':') { 351 if (split) { 352 break; 353 } 354 split = cp; 355 } 356 } 357 if (*cp || !split) { 358 continue; 359 } 360 361 /* Filters */ 362 if (prefix_len && strncmp(msg + sizeof(prio), prefix, prefix_len)) { 363 size_t offset; 364 /* 365 * Allow : to be a synonym for / 366 * Things we do dealing with const char * and do not alloc 367 */ 368 split = strchr(prefix, ':'); 369 if (split) { 370 continue; 371 } 372 split = strchr(prefix, '/'); 373 if (!split) { 374 continue; 375 } 376 offset = split - prefix; 377 if ((msg[offset + sizeof(prio)] != ':') || 378 strncmp(msg + sizeof(prio), prefix, offset)) { 379 continue; 380 } 381 ++offset; 382 if ((prefix_len > offset) && 383 strncmp(&msg[offset + sizeof(prio)], split + 1, prefix_len - offset)) { 384 continue; 385 } 386 } 387 388 if ((prio != ANDROID_LOG_ANY) && (*msg < prio)) { 389 continue; 390 } 391 392 /* check if there is an existing entry */ 393 list_for_each(node, &name_list) { 394 names = node_to_item(node, struct names, node); 395 if (!strcmp(names->name, msg + sizeof(prio)) && 396 (names->id == transp.logMsg.entry.lid) && 397 (names->prio == *msg)) { 398 break; 399 } 400 } 401 402 /* We do not have an existing entry, create and add one */ 403 if (node == &name_list) { 404 static const char numbers[] = "0123456789"; 405 unsigned long long nl; 406 407 len = strlen(msg + sizeof(prio)) + 1; 408 names = calloc(1, sizeof(*names) + len); 409 if (!names) { 410 ret = -ENOMEM; 411 break; 412 } 413 strcpy(names->name, msg + sizeof(prio)); 414 names->id = transp.logMsg.entry.lid; 415 names->prio = *msg; 416 list_init(&names->content); 417 /* 418 * Insert in reverse numeric _then_ alpha sorted order as 419 * representative of log rotation: 420 * 421 * log.10 422 * klog.10 423 * . . . 424 * log.2 425 * klog.2 426 * log.1 427 * klog.1 428 * log 429 * klog 430 * 431 * thus when we present the content, we are provided the oldest 432 * first, which when 'refreshed' could spill off the end of the 433 * pmsg FIFO but retaining the newest data for last with best 434 * chances to survive. 435 */ 436 nl = 0; 437 cp = strpbrk(names->name, numbers); 438 if (cp) { 439 nl = strtoull(cp, NULL, 10); 440 } 441 list_for_each_reverse(node, &name_list) { 442 struct names *a_name = node_to_item(node, struct names, node); 443 const char *r = a_name->name; 444 int compare = 0; 445 446 unsigned long long nr = 0; 447 cp = strpbrk(r, numbers); 448 if (cp) { 449 nr = strtoull(cp, NULL, 10); 450 } 451 if (nr != nl) { 452 compare = (nl > nr) ? 1 : -1; 453 } 454 if (compare == 0) { 455 compare = strcmp(names->name, r); 456 } 457 if (compare <= 0) { 458 break; 459 } 460 } 461 list_add_head(node, &names->node); 462 } 463 464 /* Remove any file fragments that match our sequence number */ 465 list_for_each_safe(node, n, &names->content) { 466 content = node_to_item(node, struct content, node); 467 if (transp.logMsg.entry.nsec == content->entry.nsec) { 468 list_remove(&content->node); 469 free(content); 470 } 471 } 472 473 /* Add content */ 474 content = calloc(1, sizeof(content->node) + 475 hdr_size + transp.logMsg.entry.len); 476 if (!content) { 477 ret = -ENOMEM; 478 break; 479 } 480 memcpy(&content->entry, &transp.logMsg.entry, 481 hdr_size + transp.logMsg.entry.len); 482 483 /* Insert in sequence number sorted order, to ease reconstruction */ 484 list_for_each_reverse(node, &names->content) { 485 if ((node_to_item(node, struct content, node))->entry.nsec < 486 transp.logMsg.entry.nsec) { 487 break; 488 } 489 } 490 list_add_head(node, &content->node); 491 } 492 pmsgClose(&logger_list, &transp); 493 494 /* Progress through all the collected files */ 495 list_for_each_safe(node, n, &name_list) { 496 struct listnode *content_node, *m; 497 char *buf; 498 size_t sequence, tag_len; 499 500 names = node_to_item(node, struct names, node); 501 502 /* Construct content into a linear buffer */ 503 buf = NULL; 504 len = 0; 505 sequence = 0; 506 tag_len = strlen(names->name) + sizeof(char); /* tag + nul */ 507 list_for_each_safe(content_node, m, &names->content) { 508 ssize_t add_len; 509 510 content = node_to_item(content_node, struct content, node); 511 add_len = content->entry.len - tag_len - sizeof(prio); 512 if (add_len <= 0) { 513 list_remove(content_node); 514 free(content); 515 continue; 516 } 517 518 if (!buf) { 519 buf = malloc(sizeof(char)); 520 if (!buf) { 521 ret = -ENOMEM; 522 list_remove(content_node); 523 free(content); 524 continue; 525 } 526 *buf = '\0'; 527 } 528 529 /* Missing sequence numbers */ 530 while (sequence < content->entry.nsec) { 531 /* plus space for enforced nul */ 532 buf = realloc(buf, len + sizeof(char) + sizeof(char)); 533 if (!buf) { 534 break; 535 } 536 buf[len] = '\f'; /* Mark missing content with a form feed */ 537 buf[++len] = '\0'; 538 sequence += ANDROID_LOG_PMSG_FILE_SEQUENCE; 539 } 540 if (!buf) { 541 ret = -ENOMEM; 542 list_remove(content_node); 543 free(content); 544 continue; 545 } 546 /* plus space for enforced nul */ 547 buf = realloc(buf, len + add_len + sizeof(char)); 548 if (!buf) { 549 ret = -ENOMEM; 550 list_remove(content_node); 551 free(content); 552 continue; 553 } 554 memcpy(buf + len, 555 (char *)&content->entry + content->entry.hdr_size + 556 tag_len + sizeof(prio), 557 add_len); 558 len += add_len; 559 buf[len] = '\0'; /* enforce trailing hidden nul */ 560 sequence = content->entry.nsec + ANDROID_LOG_PMSG_FILE_SEQUENCE; 561 562 list_remove(content_node); 563 free(content); 564 } 565 if (buf) { 566 if (len) { 567 /* Buffer contains enforced trailing nul just beyond length */ 568 ssize_t r; 569 *strchr(names->name, ':') = '/'; /* Convert back to filename */ 570 r = (*fn)(names->id, names->prio, names->name, buf, len, arg); 571 if ((ret >= 0) && (r > 0)) { 572 if (ret == SSIZE_MAX) { 573 ret = r; 574 } else { 575 ret += r; 576 } 577 } else if (r < ret) { 578 ret = r; 579 } 580 } 581 free(buf); 582 } 583 list_remove(node); 584 free(names); 585 } 586 return (ret == SSIZE_MAX) ? -ENOENT : ret; 587 } 588