Home | History | Annotate | Download | only in liblog
      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