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
31static int pmsgAvailable(log_id_t logId);
32static int pmsgVersion(struct android_log_logger *logger,
33                       struct android_log_transport_context *transp);
34static int pmsgRead(struct android_log_logger_list *logger_list,
35                    struct android_log_transport_context *transp,
36                    struct log_msg *log_msg);
37static void pmsgClose(struct android_log_logger_list *logger_list,
38                      struct android_log_transport_context *transp);
39static int pmsgClear(struct android_log_logger *logger,
40                     struct android_log_transport_context *transp);
41
42LIBLOG_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
59static 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 */
71static bool uid_has_log_permission(uid_t uid)
72{
73    return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT);
74}
75
76static 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
117static 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 */
130static int pmsgVersion(struct android_log_logger *logger __unused,
131                       struct android_log_transport_context *transp __unused)
132{
133    return 4;
134}
135
136static 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
251static 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
259LIBLOG_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