1// Copyright 2006-2015 The Android Open Source Project
2
3#include <arpa/inet.h>
4#include <assert.h>
5#include <ctype.h>
6#include <dirent.h>
7#include <errno.h>
8#include <fcntl.h>
9#include <getopt.h>
10#include <math.h>
11#include <sched.h>
12#include <signal.h>
13#include <stdarg.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <sys/cdefs.h>
18#include <sys/resource.h>
19#include <sys/socket.h>
20#include <sys/stat.h>
21#include <sys/types.h>
22#include <time.h>
23#include <unistd.h>
24
25#include <memory>
26#include <string>
27
28#include <android-base/file.h>
29#include <android-base/strings.h>
30#include <cutils/sched_policy.h>
31#include <cutils/sockets.h>
32#include <log/event_tag_map.h>
33#include <log/log.h>
34#include <log/log_read.h>
35#include <log/logd.h>
36#include <log/logger.h>
37#include <log/logprint.h>
38#include <utils/threads.h>
39
40#include <pcrecpp.h>
41
42#define DEFAULT_MAX_ROTATED_LOGS 4
43
44static AndroidLogFormat * g_logformat;
45
46/* logd prefixes records with a length field */
47#define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t)
48
49struct log_device_t {
50    const char* device;
51    bool binary;
52    struct logger *logger;
53    struct logger_list *logger_list;
54    bool printed;
55
56    log_device_t* next;
57
58    log_device_t(const char* d, bool b) {
59        device = d;
60        binary = b;
61        next = NULL;
62        printed = false;
63        logger = NULL;
64        logger_list = NULL;
65    }
66};
67
68namespace android {
69
70/* Global Variables */
71
72static const char * g_outputFileName;
73// 0 means "no log rotation"
74static size_t g_logRotateSizeKBytes;
75// 0 means "unbounded"
76static size_t g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS;
77static int g_outFD = -1;
78static size_t g_outByteCount;
79static int g_printBinary;
80static int g_devCount;                              // >1 means multiple
81static pcrecpp::RE* g_regex;
82// 0 means "infinite"
83static size_t g_maxCount;
84static size_t g_printCount;
85static bool g_printItAnyways;
86
87// if showHelp is set, newline required in fmt statement to transition to usage
88__noreturn static void logcat_panic(bool showHelp, const char *fmt, ...) __printflike(2,3);
89
90static int openLogFile (const char *pathname)
91{
92    return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
93}
94
95static void rotateLogs()
96{
97    int err;
98
99    // Can't rotate logs if we're not outputting to a file
100    if (g_outputFileName == NULL) {
101        return;
102    }
103
104    close(g_outFD);
105
106    // Compute the maximum number of digits needed to count up to g_maxRotatedLogs in decimal.
107    // eg: g_maxRotatedLogs == 30 -> log10(30) == 1.477 -> maxRotationCountDigits == 2
108    int maxRotationCountDigits =
109            (g_maxRotatedLogs > 0) ? (int) (floor(log10(g_maxRotatedLogs) + 1)) : 0;
110
111    for (int i = g_maxRotatedLogs ; i > 0 ; i--) {
112        char *file0, *file1;
113
114        asprintf(&file1, "%s.%.*d", g_outputFileName, maxRotationCountDigits, i);
115
116        if (i - 1 == 0) {
117            asprintf(&file0, "%s", g_outputFileName);
118        } else {
119            asprintf(&file0, "%s.%.*d", g_outputFileName, maxRotationCountDigits, i - 1);
120        }
121
122        if (!file0 || !file1) {
123            perror("while rotating log files");
124            break;
125        }
126
127        err = rename(file0, file1);
128
129        if (err < 0 && errno != ENOENT) {
130            perror("while rotating log files");
131        }
132
133        free(file1);
134        free(file0);
135    }
136
137    g_outFD = openLogFile(g_outputFileName);
138
139    if (g_outFD < 0) {
140        logcat_panic(false, "couldn't open output file");
141    }
142
143    g_outByteCount = 0;
144
145}
146
147void printBinary(struct log_msg *buf)
148{
149    size_t size = buf->len();
150
151    TEMP_FAILURE_RETRY(write(g_outFD, buf, size));
152}
153
154static bool regexOk(const AndroidLogEntry& entry)
155{
156    if (!g_regex) {
157        return true;
158    }
159
160    std::string messageString(entry.message, entry.messageLen);
161
162    return g_regex->PartialMatch(messageString);
163}
164
165static void processBuffer(log_device_t* dev, struct log_msg *buf)
166{
167    int bytesWritten = 0;
168    int err;
169    AndroidLogEntry entry;
170    char binaryMsgBuf[1024];
171
172    if (dev->binary) {
173        static bool hasOpenedEventTagMap = false;
174        static EventTagMap *eventTagMap = NULL;
175
176        if (!eventTagMap && !hasOpenedEventTagMap) {
177            eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE);
178            hasOpenedEventTagMap = true;
179        }
180        err = android_log_processBinaryLogBuffer(&buf->entry_v1, &entry,
181                                                 eventTagMap,
182                                                 binaryMsgBuf,
183                                                 sizeof(binaryMsgBuf));
184        //printf(">>> pri=%d len=%d msg='%s'\n",
185        //    entry.priority, entry.messageLen, entry.message);
186    } else {
187        err = android_log_processLogBuffer(&buf->entry_v1, &entry);
188    }
189    if (err < 0) {
190        goto error;
191    }
192
193    if (android_log_shouldPrintLine(g_logformat, entry.tag, entry.priority)) {
194        bool match = regexOk(entry);
195
196        g_printCount += match;
197        if (match || g_printItAnyways) {
198            bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry);
199
200            if (bytesWritten < 0) {
201                logcat_panic(false, "output error");
202            }
203        }
204    }
205
206    g_outByteCount += bytesWritten;
207
208    if (g_logRotateSizeKBytes > 0
209        && (g_outByteCount / 1024) >= g_logRotateSizeKBytes
210    ) {
211        rotateLogs();
212    }
213
214error:
215    //fprintf (stderr, "Error processing record\n");
216    return;
217}
218
219static void maybePrintStart(log_device_t* dev, bool printDividers) {
220    if (!dev->printed || printDividers) {
221        if (g_devCount > 1 && !g_printBinary) {
222            char buf[1024];
223            snprintf(buf, sizeof(buf), "--------- %s %s\n",
224                     dev->printed ? "switch to" : "beginning of",
225                     dev->device);
226            if (write(g_outFD, buf, strlen(buf)) < 0) {
227                logcat_panic(false, "output error");
228            }
229        }
230        dev->printed = true;
231    }
232}
233
234static void setupOutput()
235{
236
237    if (g_outputFileName == NULL) {
238        g_outFD = STDOUT_FILENO;
239
240    } else {
241        if (set_sched_policy(0, SP_BACKGROUND) < 0) {
242            fprintf(stderr, "failed to set background scheduling policy\n");
243        }
244
245        struct sched_param param;
246        memset(&param, 0, sizeof(param));
247        if (sched_setscheduler((pid_t) 0, SCHED_BATCH, &param) < 0) {
248            fprintf(stderr, "failed to set to batch scheduler\n");
249        }
250
251        if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
252            fprintf(stderr, "failed set to priority\n");
253        }
254
255        g_outFD = openLogFile (g_outputFileName);
256
257        if (g_outFD < 0) {
258            logcat_panic(false, "couldn't open output file");
259        }
260
261        struct stat statbuf;
262        if (fstat(g_outFD, &statbuf) == -1) {
263            close(g_outFD);
264            logcat_panic(false, "couldn't get output file stat\n");
265        }
266
267        if ((size_t) statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
268            close(g_outFD);
269            logcat_panic(false, "invalid output file stat\n");
270        }
271
272        g_outByteCount = statbuf.st_size;
273    }
274}
275
276static void show_help(const char *cmd)
277{
278    fprintf(stderr,"Usage: %s [options] [filterspecs]\n", cmd);
279
280    fprintf(stderr, "options include:\n"
281                    "  -s              Set default filter to silent.\n"
282                    "                  Like specifying filterspec '*:S'\n"
283                    "  -f <filename>   Log to file. Default is stdout\n"
284                    "  --file=<filename>\n"
285                    "  -r <kbytes>     Rotate log every kbytes. Requires -f\n"
286                    "  --rotate-kbytes=<kbytes>\n"
287                    "  -n <count>      Sets max number of rotated logs to <count>, default 4\n"
288                    "  --rotate-count=<count>\n"
289                    "  -v <format>     Sets the log print format, where <format> is:\n"
290                    "  --format=<format>\n"
291                    "                      brief color epoch long monotonic printable process raw\n"
292                    "                      tag thread threadtime time uid usec UTC year zone\n\n"
293                    "  -D              print dividers between each log buffer\n"
294                    "  --dividers\n"
295                    "  -c              clear (flush) the entire log and exit\n"
296                    "  --clear\n"
297                    "  -d              dump the log and then exit (don't block)\n"
298                    "  -e <expr>       only print lines where the log message matches <expr>\n"
299                    "  --regex <expr>  where <expr> is a regular expression\n"
300                    "  -m <count>      quit after printing <count> lines. This is meant to be\n"
301                    "  --max-count=<count> paired with --regex, but will work on its own.\n"
302                    "  --print         paired with --regex and --max-count to let content bypass\n"
303                    "                  regex filter but still stop at number of matches.\n"
304                    "  -t <count>      print only the most recent <count> lines (implies -d)\n"
305                    "  -t '<time>'     print most recent lines since specified time (implies -d)\n"
306                    "  -T <count>      print only the most recent <count> lines (does not imply -d)\n"
307                    "  -T '<time>'     print most recent lines since specified time (not imply -d)\n"
308                    "                  count is pure numerical, time is 'MM-DD hh:mm:ss.mmm...'\n"
309                    "                  'YYYY-MM-DD hh:mm:ss.mmm...' or 'sssss.mmm...' format\n"
310                    "  -g              get the size of the log's ring buffer and exit\n"
311                    "  --buffer-size\n"
312                    "  -G <size>       set size of log ring buffer, may suffix with K or M.\n"
313                    "  --buffer-size=<size>\n"
314                    "  -L              dump logs from prior to last reboot\n"
315                    "  --last\n"
316                    // Leave security (Device Owner only installations) and
317                    // kernel (userdebug and eng) buffers undocumented.
318                    "  -b <buffer>     Request alternate ring buffer, 'main', 'system', 'radio',\n"
319                    "  --buffer=<buffer> 'events', 'crash', 'default' or 'all'. Multiple -b\n"
320                    "                  parameters are allowed and results are interleaved. The\n"
321                    "                  default is -b main -b system -b crash.\n"
322                    "  -B              output the log in binary.\n"
323                    "  --binary\n"
324                    "  -S              output statistics.\n"
325                    "  --statistics\n"
326                    "  -p              print prune white and ~black list. Service is specified as\n"
327                    "  --prune         UID, UID/PID or /PID. Weighed for quicker pruning if prefix\n"
328                    "                  with ~, otherwise weighed for longevity if unadorned. All\n"
329                    "                  other pruning activity is oldest first. Special case ~!\n"
330                    "                  represents an automatic quicker pruning for the noisiest\n"
331                    "                  UID as determined by the current statistics.\n"
332                    "  -P '<list> ...' set prune white and ~black list, using same format as\n"
333                    "  --prune='<list> ...'  printed above. Must be quoted.\n"
334                    "  --pid=<pid>     Only prints logs from the given pid.\n"
335                    // Check ANDROID_LOG_WRAP_DEFAULT_TIMEOUT value
336                    "  --wrap          Sleep for 2 hours or when buffer about to wrap whichever\n"
337                    "                  comes first. Improves efficiency of polling by providing\n"
338                    "                  an about-to-wrap wakeup.\n");
339
340    fprintf(stderr,"\nfilterspecs are a series of \n"
341                   "  <tag>[:priority]\n\n"
342                   "where <tag> is a log component tag (or * for all) and priority is:\n"
343                   "  V    Verbose (default for <tag>)\n"
344                   "  D    Debug (default for '*')\n"
345                   "  I    Info\n"
346                   "  W    Warn\n"
347                   "  E    Error\n"
348                   "  F    Fatal\n"
349                   "  S    Silent (suppress all output)\n"
350                   "\n'*' by itself means '*:D' and <tag> by itself means <tag>:V.\n"
351                   "If no '*' filterspec or -s on command line, all filter defaults to '*:V'.\n"
352                   "eg: '*:S <tag>' prints only <tag>, '<tag>:S' suppresses all <tag> log messages.\n"
353                   "\nIf not specified on the command line, filterspec is set from ANDROID_LOG_TAGS.\n"
354                   "\nIf not specified with -v on command line, format is set from ANDROID_PRINTF_LOG\n"
355                   "or defaults to \"threadtime\"\n\n");
356}
357
358static int setLogFormat(const char * formatString)
359{
360    static AndroidLogPrintFormat format;
361
362    format = android_log_formatFromString(formatString);
363
364    if (format == FORMAT_OFF) {
365        // FORMAT_OFF means invalid string
366        return -1;
367    }
368
369    return android_log_setPrintFormat(g_logformat, format);
370}
371
372static const char multipliers[][2] = {
373    { "" },
374    { "K" },
375    { "M" },
376    { "G" }
377};
378
379static unsigned long value_of_size(unsigned long value)
380{
381    for (unsigned i = 0;
382            (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024);
383            value /= 1024, ++i) ;
384    return value;
385}
386
387static const char *multiplier_of_size(unsigned long value)
388{
389    unsigned i;
390    for (i = 0;
391            (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024);
392            value /= 1024, ++i) ;
393    return multipliers[i];
394}
395
396/*String to unsigned int, returns -1 if it fails*/
397static bool getSizeTArg(char *ptr, size_t *val, size_t min = 0,
398                        size_t max = SIZE_MAX)
399{
400    if (!ptr) {
401        return false;
402    }
403
404    char *endp;
405    errno = 0;
406    size_t ret = (size_t)strtoll(ptr, &endp, 0);
407
408    if (endp[0] || errno) {
409        return false;
410    }
411
412    if ((ret > max) || (ret < min)) {
413        return false;
414    }
415
416    *val = ret;
417    return true;
418}
419
420static void logcat_panic(bool showHelp, const char *fmt, ...)
421{
422    va_list  args;
423    va_start(args, fmt);
424    vfprintf(stderr, fmt,  args);
425    va_end(args);
426
427    if (showHelp) {
428       show_help(getprogname());
429    }
430
431    exit(EXIT_FAILURE);
432}
433
434static char *parseTime(log_time &t, const char *cp) {
435
436    char *ep = t.strptime(cp, "%m-%d %H:%M:%S.%q");
437    if (ep) {
438        return ep;
439    }
440    ep = t.strptime(cp, "%Y-%m-%d %H:%M:%S.%q");
441    if (ep) {
442        return ep;
443    }
444    return t.strptime(cp, "%s.%q");
445}
446
447// Find last logged line in gestalt of all matching existing output files
448static log_time lastLogTime(char *outputFileName) {
449    log_time retval(log_time::EPOCH);
450    if (!outputFileName) {
451        return retval;
452    }
453
454    std::string directory;
455    char *file = strrchr(outputFileName, '/');
456    if (!file) {
457        directory = ".";
458        file = outputFileName;
459    } else {
460        *file = '\0';
461        directory = outputFileName;
462        *file = '/';
463        ++file;
464    }
465
466    std::unique_ptr<DIR, int(*)(DIR*)>
467            dir(opendir(directory.c_str()), closedir);
468    if (!dir.get()) {
469        return retval;
470    }
471
472    clockid_t clock_type = android_log_clockid();
473    log_time now(clock_type);
474    bool monotonic = clock_type == CLOCK_MONOTONIC;
475
476    size_t len = strlen(file);
477    log_time modulo(0, NS_PER_SEC);
478    struct dirent *dp;
479
480    while ((dp = readdir(dir.get())) != NULL) {
481        if ((dp->d_type != DT_REG)
482                // If we are using realtime, check all files that match the
483                // basename for latest time. If we are using monotonic time
484                // then only check the main file because time cycles on
485                // every reboot.
486                || strncmp(dp->d_name, file, len + monotonic)
487                || (dp->d_name[len]
488                    && ((dp->d_name[len] != '.')
489                        || !isdigit(dp->d_name[len+1])))) {
490            continue;
491        }
492
493        std::string file_name = directory;
494        file_name += "/";
495        file_name += dp->d_name;
496        std::string file;
497        if (!android::base::ReadFileToString(file_name, &file)) {
498            continue;
499        }
500
501        bool found = false;
502        for (const auto& line : android::base::Split(file, "\n")) {
503            log_time t(log_time::EPOCH);
504            char *ep = parseTime(t, line.c_str());
505            if (!ep || (*ep != ' ')) {
506                continue;
507            }
508            // determine the time precision of the logs (eg: msec or usec)
509            for (unsigned long mod = 1UL; mod < modulo.tv_nsec; mod *= 10) {
510                if (t.tv_nsec % (mod * 10)) {
511                    modulo.tv_nsec = mod;
512                    break;
513                }
514            }
515            // We filter any times later than current as we may not have the
516            // year stored with each log entry. Also, since it is possible for
517            // entries to be recorded out of order (very rare) we select the
518            // maximum we find just in case.
519            if ((t < now) && (t > retval)) {
520                retval = t;
521                found = true;
522            }
523        }
524        // We count on the basename file to be the definitive end, so stop here.
525        if (!dp->d_name[len] && found) {
526            break;
527        }
528    }
529    if (retval == log_time::EPOCH) {
530        return retval;
531    }
532    // tail_time prints matching or higher, round up by the modulo to prevent
533    // a replay of the last entry we have just checked.
534    retval += modulo;
535    return retval;
536}
537
538} /* namespace android */
539
540
541int main(int argc, char **argv)
542{
543    using namespace android;
544    int err;
545    int hasSetLogFormat = 0;
546    int clearLog = 0;
547    int getLogSize = 0;
548    unsigned long setLogSize = 0;
549    int getPruneList = 0;
550    char *setPruneList = NULL;
551    int printStatistics = 0;
552    int mode = ANDROID_LOG_RDONLY;
553    const char *forceFilters = NULL;
554    log_device_t* devices = NULL;
555    log_device_t* dev;
556    bool printDividers = false;
557    struct logger_list *logger_list;
558    size_t tail_lines = 0;
559    log_time tail_time(log_time::EPOCH);
560    size_t pid = 0;
561    bool got_t = false;
562
563    signal(SIGPIPE, exit);
564
565    g_logformat = android_log_format_new();
566
567    if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
568        show_help(argv[0]);
569        return EXIT_SUCCESS;
570    }
571
572    for (;;) {
573        int ret;
574
575        int option_index = 0;
576        // list of long-argument only strings for later comparison
577        static const char pid_str[] = "pid";
578        static const char wrap_str[] = "wrap";
579        static const char print_str[] = "print";
580        static const struct option long_options[] = {
581          { "binary",        no_argument,       NULL,   'B' },
582          { "buffer",        required_argument, NULL,   'b' },
583          { "buffer-size",   optional_argument, NULL,   'g' },
584          { "clear",         no_argument,       NULL,   'c' },
585          { "dividers",      no_argument,       NULL,   'D' },
586          { "file",          required_argument, NULL,   'f' },
587          { "format",        required_argument, NULL,   'v' },
588          // hidden and undocumented reserved alias for --regex
589          { "grep",          required_argument, NULL,   'e' },
590          // hidden and undocumented reserved alias for --max-count
591          { "head",          required_argument, NULL,   'm' },
592          { "last",          no_argument,       NULL,   'L' },
593          { "max-count",     required_argument, NULL,   'm' },
594          { pid_str,         required_argument, NULL,   0 },
595          { print_str,       no_argument,       NULL,   0 },
596          { "prune",         optional_argument, NULL,   'p' },
597          { "regex",         required_argument, NULL,   'e' },
598          { "rotate-count",  required_argument, NULL,   'n' },
599          { "rotate-kbytes", required_argument, NULL,   'r' },
600          { "statistics",    no_argument,       NULL,   'S' },
601          // hidden and undocumented reserved alias for -t
602          { "tail",          required_argument, NULL,   't' },
603          // support, but ignore and do not document, the optional argument
604          { wrap_str,        optional_argument, NULL,   0 },
605          { NULL,            0,                 NULL,   0 }
606        };
607
608        ret = getopt_long(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:",
609                          long_options, &option_index);
610
611        if (ret < 0) {
612            break;
613        }
614
615        switch (ret) {
616            case 0:
617                // One of the long options
618                if (long_options[option_index].name == pid_str) {
619                    // ToDo: determine runtime PID_MAX?
620                    if (!getSizeTArg(optarg, &pid, 1)) {
621                        logcat_panic(true, "%s %s out of range\n",
622                                     long_options[option_index].name, optarg);
623                    }
624                    break;
625                }
626                if (long_options[option_index].name == wrap_str) {
627                    mode |= ANDROID_LOG_WRAP |
628                            ANDROID_LOG_RDONLY |
629                            ANDROID_LOG_NONBLOCK;
630                    // ToDo: implement API that supports setting a wrap timeout
631                    size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
632                    if (optarg && !getSizeTArg(optarg, &dummy, 1)) {
633                        logcat_panic(true, "%s %s out of range\n",
634                                     long_options[option_index].name, optarg);
635                    }
636                    if (dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) {
637                        fprintf(stderr,
638                                "WARNING: %s %u seconds, ignoring %zu\n",
639                                long_options[option_index].name,
640                                ANDROID_LOG_WRAP_DEFAULT_TIMEOUT, dummy);
641                    }
642                    break;
643                }
644                if (long_options[option_index].name == print_str) {
645                    g_printItAnyways = true;
646                    break;
647                }
648            break;
649
650            case 's':
651                // default to all silent
652                android_log_addFilterRule(g_logformat, "*:s");
653            break;
654
655            case 'c':
656                clearLog = 1;
657                mode |= ANDROID_LOG_WRONLY;
658            break;
659
660            case 'L':
661                mode |= ANDROID_LOG_PSTORE;
662            break;
663
664            case 'd':
665                mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
666            break;
667
668            case 't':
669                got_t = true;
670                mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
671                /* FALLTHRU */
672            case 'T':
673                if (strspn(optarg, "0123456789") != strlen(optarg)) {
674                    char *cp = parseTime(tail_time, optarg);
675                    if (!cp) {
676                        logcat_panic(false, "-%c \"%s\" not in time format\n",
677                                     ret, optarg);
678                    }
679                    if (*cp) {
680                        char c = *cp;
681                        *cp = '\0';
682                        fprintf(stderr,
683                                "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
684                                ret, optarg, c, cp + 1);
685                        *cp = c;
686                    }
687                } else {
688                    if (!getSizeTArg(optarg, &tail_lines, 1)) {
689                        fprintf(stderr,
690                                "WARNING: -%c %s invalid, setting to 1\n",
691                                ret, optarg);
692                        tail_lines = 1;
693                    }
694                }
695            break;
696
697            case 'D':
698                printDividers = true;
699            break;
700
701            case 'e':
702                g_regex = new pcrecpp::RE(optarg);
703            break;
704
705            case 'm': {
706                char *end = NULL;
707                if (!getSizeTArg(optarg, &g_maxCount)) {
708                    logcat_panic(false, "-%c \"%s\" isn't an "
709                                 "integer greater than zero\n", ret, optarg);
710                }
711            }
712            break;
713
714            case 'g':
715                if (!optarg) {
716                    getLogSize = 1;
717                    break;
718                }
719                // FALLTHRU
720
721            case 'G': {
722                char *cp;
723                if (strtoll(optarg, &cp, 0) > 0) {
724                    setLogSize = strtoll(optarg, &cp, 0);
725                } else {
726                    setLogSize = 0;
727                }
728
729                switch(*cp) {
730                case 'g':
731                case 'G':
732                    setLogSize *= 1024;
733                /* FALLTHRU */
734                case 'm':
735                case 'M':
736                    setLogSize *= 1024;
737                /* FALLTHRU */
738                case 'k':
739                case 'K':
740                    setLogSize *= 1024;
741                /* FALLTHRU */
742                case '\0':
743                break;
744
745                default:
746                    setLogSize = 0;
747                }
748
749                if (!setLogSize) {
750                    fprintf(stderr, "ERROR: -G <num><multiplier>\n");
751                    return EXIT_FAILURE;
752                }
753            }
754            break;
755
756            case 'p':
757                if (!optarg) {
758                    getPruneList = 1;
759                    break;
760                }
761                // FALLTHRU
762
763            case 'P':
764                setPruneList = optarg;
765            break;
766
767            case 'b': {
768                if (strcmp(optarg, "default") == 0) {
769                    for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
770                        switch (i) {
771                        case LOG_ID_SECURITY:
772                        case LOG_ID_EVENTS:
773                            continue;
774                        case LOG_ID_MAIN:
775                        case LOG_ID_SYSTEM:
776                        case LOG_ID_CRASH:
777                            break;
778                        default:
779                            continue;
780                        }
781
782                        const char *name = android_log_id_to_name((log_id_t)i);
783                        log_id_t log_id = android_name_to_log_id(name);
784
785                        if (log_id != (log_id_t)i) {
786                            continue;
787                        }
788
789                        bool found = false;
790                        for (dev = devices; dev; dev = dev->next) {
791                            if (!strcmp(optarg, dev->device)) {
792                                found = true;
793                                break;
794                            }
795                            if (!dev->next) {
796                                break;
797                            }
798                        }
799                        if (found) {
800                            break;
801                        }
802
803                        log_device_t* d = new log_device_t(name, false);
804
805                        if (dev) {
806                            dev->next = d;
807                            dev = d;
808                        } else {
809                            devices = dev = d;
810                        }
811                        g_devCount++;
812                    }
813                    break;
814                }
815
816                if (strcmp(optarg, "all") == 0) {
817                    for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
818                        const char *name = android_log_id_to_name((log_id_t)i);
819                        log_id_t log_id = android_name_to_log_id(name);
820
821                        if (log_id != (log_id_t)i) {
822                            continue;
823                        }
824
825                        bool found = false;
826                        for (dev = devices; dev; dev = dev->next) {
827                            if (!strcmp(optarg, dev->device)) {
828                                found = true;
829                                break;
830                            }
831                            if (!dev->next) {
832                                break;
833                            }
834                        }
835                        if (found) {
836                            break;
837                        }
838
839                        bool binary = !strcmp(name, "events") ||
840                                      !strcmp(name, "security");
841                        log_device_t* d = new log_device_t(name, binary);
842
843                        if (dev) {
844                            dev->next = d;
845                            dev = d;
846                        } else {
847                            devices = dev = d;
848                        }
849                        g_devCount++;
850                    }
851                    break;
852                }
853
854                bool binary = !(strcmp(optarg, "events") &&
855                                strcmp(optarg, "security"));
856
857                if (devices) {
858                    dev = devices;
859                    while (dev->next) {
860                        if (!strcmp(optarg, dev->device)) {
861                            dev = NULL;
862                            break;
863                        }
864                        dev = dev->next;
865                    }
866                    if (dev) {
867                        dev->next = new log_device_t(optarg, binary);
868                    }
869                } else {
870                    devices = new log_device_t(optarg, binary);
871                }
872                g_devCount++;
873            }
874            break;
875
876            case 'B':
877                g_printBinary = 1;
878            break;
879
880            case 'f':
881                if ((tail_time == log_time::EPOCH) && (tail_lines == 0)) {
882                    tail_time = lastLogTime(optarg);
883                }
884                // redirect output to a file
885                g_outputFileName = optarg;
886            break;
887
888            case 'r':
889                if (!getSizeTArg(optarg, &g_logRotateSizeKBytes, 1)) {
890                    logcat_panic(true, "Invalid parameter %s to -r\n", optarg);
891                }
892            break;
893
894            case 'n':
895                if (!getSizeTArg(optarg, &g_maxRotatedLogs, 1)) {
896                    logcat_panic(true, "Invalid parameter %s to -n\n", optarg);
897                }
898            break;
899
900            case 'v':
901                err = setLogFormat (optarg);
902                if (err < 0) {
903                    logcat_panic(true, "Invalid parameter %s to -v\n", optarg);
904                }
905                hasSetLogFormat |= err;
906            break;
907
908            case 'Q':
909                /* this is a *hidden* option used to start a version of logcat                 */
910                /* in an emulated device only. it basically looks for androidboot.logcat=      */
911                /* on the kernel command line. If something is found, it extracts a log filter */
912                /* and uses it to run the program. If nothing is found, the program should     */
913                /* quit immediately                                                            */
914#define  KERNEL_OPTION  "androidboot.logcat="
915#define  CONSOLE_OPTION "androidboot.console="
916                {
917                    int          fd;
918                    char*        logcat;
919                    char*        console;
920                    int          force_exit = 1;
921                    static char  cmdline[1024];
922
923                    fd = open("/proc/cmdline", O_RDONLY);
924                    if (fd >= 0) {
925                        int  n = read(fd, cmdline, sizeof(cmdline)-1 );
926                        if (n < 0) n = 0;
927                        cmdline[n] = 0;
928                        close(fd);
929                    } else {
930                        cmdline[0] = 0;
931                    }
932
933                    logcat  = strstr( cmdline, KERNEL_OPTION );
934                    console = strstr( cmdline, CONSOLE_OPTION );
935                    if (logcat != NULL) {
936                        char*  p = logcat + sizeof(KERNEL_OPTION)-1;;
937                        char*  q = strpbrk( p, " \t\n\r" );;
938
939                        if (q != NULL)
940                            *q = 0;
941
942                        forceFilters = p;
943                        force_exit   = 0;
944                    }
945                    /* if nothing found or invalid filters, exit quietly */
946                    if (force_exit) {
947                        return EXIT_SUCCESS;
948                    }
949
950                    /* redirect our output to the emulator console */
951                    if (console) {
952                        char*  p = console + sizeof(CONSOLE_OPTION)-1;
953                        char*  q = strpbrk( p, " \t\n\r" );
954                        char   devname[64];
955                        int    len;
956
957                        if (q != NULL) {
958                            len = q - p;
959                        } else
960                            len = strlen(p);
961
962                        len = snprintf( devname, sizeof(devname), "/dev/%.*s", len, p );
963                        fprintf(stderr, "logcat using %s (%d)\n", devname, len);
964                        if (len < (int)sizeof(devname)) {
965                            fd = open( devname, O_WRONLY );
966                            if (fd >= 0) {
967                                dup2(fd, 1);
968                                dup2(fd, 2);
969                                close(fd);
970                            }
971                        }
972                    }
973                }
974                break;
975
976            case 'S':
977                printStatistics = 1;
978                break;
979
980            case ':':
981                logcat_panic(true, "Option -%c needs an argument\n", optopt);
982                break;
983
984            default:
985                logcat_panic(true, "Unrecognized Option %c\n", optopt);
986                break;
987        }
988    }
989
990    if (g_maxCount && got_t) {
991        logcat_panic(true, "Cannot use -m (--max-count) and -t together\n");
992    }
993    if (g_printItAnyways && (!g_regex || !g_maxCount)) {
994        // One day it would be nice if --print -v color and --regex <expr>
995        // could play with each other and show regex highlighted content.
996        fprintf(stderr, "WARNING: "
997                            "--print ignored, to be used in combination with\n"
998                        "         "
999                            "--regex <expr> and --max-count <N>\n");
1000        g_printItAnyways = false;
1001    }
1002
1003    if (!devices) {
1004        dev = devices = new log_device_t("main", false);
1005        g_devCount = 1;
1006        if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
1007            dev = dev->next = new log_device_t("system", false);
1008            g_devCount++;
1009        }
1010        if (android_name_to_log_id("crash") == LOG_ID_CRASH) {
1011            dev = dev->next = new log_device_t("crash", false);
1012            g_devCount++;
1013        }
1014    }
1015
1016    if (g_logRotateSizeKBytes != 0 && g_outputFileName == NULL) {
1017        logcat_panic(true, "-r requires -f as well\n");
1018    }
1019
1020    setupOutput();
1021
1022    if (hasSetLogFormat == 0) {
1023        const char* logFormat = getenv("ANDROID_PRINTF_LOG");
1024
1025        if (logFormat != NULL) {
1026            err = setLogFormat(logFormat);
1027            if (err < 0) {
1028                fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n",
1029                                    logFormat);
1030            }
1031        } else {
1032            setLogFormat("threadtime");
1033        }
1034    }
1035
1036    if (forceFilters) {
1037        err = android_log_addFilterString(g_logformat, forceFilters);
1038        if (err < 0) {
1039            logcat_panic(false, "Invalid filter expression in logcat args\n");
1040        }
1041    } else if (argc == optind) {
1042        // Add from environment variable
1043        char *env_tags_orig = getenv("ANDROID_LOG_TAGS");
1044
1045        if (env_tags_orig != NULL) {
1046            err = android_log_addFilterString(g_logformat, env_tags_orig);
1047
1048            if (err < 0) {
1049                logcat_panic(true,
1050                            "Invalid filter expression in ANDROID_LOG_TAGS\n");
1051            }
1052        }
1053    } else {
1054        // Add from commandline
1055        for (int i = optind ; i < argc ; i++) {
1056            err = android_log_addFilterString(g_logformat, argv[i]);
1057
1058            if (err < 0) {
1059                logcat_panic(true, "Invalid filter expression '%s'\n", argv[i]);
1060            }
1061        }
1062    }
1063
1064    dev = devices;
1065    if (tail_time != log_time::EPOCH) {
1066        logger_list = android_logger_list_alloc_time(mode, tail_time, pid);
1067    } else {
1068        logger_list = android_logger_list_alloc(mode, tail_lines, pid);
1069    }
1070    const char *openDeviceFail = NULL;
1071    const char *clearFail = NULL;
1072    const char *setSizeFail = NULL;
1073    const char *getSizeFail = NULL;
1074    // We have three orthogonal actions below to clear, set log size and
1075    // get log size. All sharing the same iteration loop.
1076    while (dev) {
1077        dev->logger_list = logger_list;
1078        dev->logger = android_logger_open(logger_list,
1079                                          android_name_to_log_id(dev->device));
1080        if (!dev->logger) {
1081            openDeviceFail = openDeviceFail ?: dev->device;
1082            dev = dev->next;
1083            continue;
1084        }
1085
1086        if (clearLog) {
1087            if (android_logger_clear(dev->logger)) {
1088                clearFail = clearFail ?: dev->device;
1089            }
1090        }
1091
1092        if (setLogSize) {
1093            if (android_logger_set_log_size(dev->logger, setLogSize)) {
1094                setSizeFail = setSizeFail ?: dev->device;
1095            }
1096        }
1097
1098        if (getLogSize) {
1099            long size = android_logger_get_log_size(dev->logger);
1100            long readable = android_logger_get_log_readable_size(dev->logger);
1101
1102            if ((size < 0) || (readable < 0)) {
1103                getSizeFail = getSizeFail ?: dev->device;
1104            } else {
1105                printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
1106                       "max entry is %db, max payload is %db\n", dev->device,
1107                       value_of_size(size), multiplier_of_size(size),
1108                       value_of_size(readable), multiplier_of_size(readable),
1109                       (int) LOGGER_ENTRY_MAX_LEN,
1110                       (int) LOGGER_ENTRY_MAX_PAYLOAD);
1111            }
1112        }
1113
1114        dev = dev->next;
1115    }
1116    // report any errors in the above loop and exit
1117    if (openDeviceFail) {
1118        logcat_panic(false, "Unable to open log device '%s'\n", openDeviceFail);
1119    }
1120    if (clearFail) {
1121        logcat_panic(false, "failed to clear the '%s' log\n", clearFail);
1122    }
1123    if (setSizeFail) {
1124        logcat_panic(false, "failed to set the '%s' log size\n", setSizeFail);
1125    }
1126    if (getSizeFail) {
1127        logcat_panic(false, "failed to get the readable '%s' log size",
1128                     getSizeFail);
1129    }
1130
1131    if (setPruneList) {
1132        size_t len = strlen(setPruneList);
1133        /*extra 32 bytes are needed by  android_logger_set_prune_list */
1134        size_t bLen = len + 32;
1135        char *buf = NULL;
1136        if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
1137            buf[len] = '\0';
1138            if (android_logger_set_prune_list(logger_list, buf, bLen)) {
1139                logcat_panic(false, "failed to set the prune list");
1140            }
1141            free(buf);
1142        } else {
1143            logcat_panic(false, "failed to set the prune list (alloc)");
1144        }
1145    }
1146
1147    if (printStatistics || getPruneList) {
1148        size_t len = 8192;
1149        char *buf;
1150
1151        for (int retry = 32;
1152                (retry >= 0) && ((buf = new char [len]));
1153                delete [] buf, buf = NULL, --retry) {
1154            if (getPruneList) {
1155                android_logger_get_prune_list(logger_list, buf, len);
1156            } else {
1157                android_logger_get_statistics(logger_list, buf, len);
1158            }
1159            buf[len-1] = '\0';
1160            if (atol(buf) < 3) {
1161                delete [] buf;
1162                buf = NULL;
1163                break;
1164            }
1165            size_t ret = atol(buf) + 1;
1166            if (ret <= len) {
1167                len = ret;
1168                break;
1169            }
1170            len = ret;
1171        }
1172
1173        if (!buf) {
1174            logcat_panic(false, "failed to read data");
1175        }
1176
1177        // remove trailing FF
1178        char *cp = buf + len - 1;
1179        *cp = '\0';
1180        bool truncated = *--cp != '\f';
1181        if (!truncated) {
1182            *cp = '\0';
1183        }
1184
1185        // squash out the byte count
1186        cp = buf;
1187        if (!truncated) {
1188            while (isdigit(*cp)) {
1189                ++cp;
1190            }
1191            if (*cp == '\n') {
1192                ++cp;
1193            }
1194        }
1195
1196        printf("%s", cp);
1197        delete [] buf;
1198        return EXIT_SUCCESS;
1199    }
1200
1201
1202    if (getLogSize) {
1203        return EXIT_SUCCESS;
1204    }
1205    if (setLogSize || setPruneList) {
1206        return EXIT_SUCCESS;
1207    }
1208    if (clearLog) {
1209        return EXIT_SUCCESS;
1210    }
1211
1212    //LOG_EVENT_INT(10, 12345);
1213    //LOG_EVENT_LONG(11, 0x1122334455667788LL);
1214    //LOG_EVENT_STRING(0, "whassup, doc?");
1215
1216    dev = NULL;
1217    log_device_t unexpected("unexpected", false);
1218
1219    while (!g_maxCount || (g_printCount < g_maxCount)) {
1220        struct log_msg log_msg;
1221        log_device_t* d;
1222        int ret = android_logger_list_read(logger_list, &log_msg);
1223
1224        if (ret == 0) {
1225            logcat_panic(false, "read: unexpected EOF!\n");
1226        }
1227
1228        if (ret < 0) {
1229            if (ret == -EAGAIN) {
1230                break;
1231            }
1232
1233            if (ret == -EIO) {
1234                logcat_panic(false, "read: unexpected EOF!\n");
1235            }
1236            if (ret == -EINVAL) {
1237                logcat_panic(false, "read: unexpected length.\n");
1238            }
1239            logcat_panic(false, "logcat read failure");
1240        }
1241
1242        for (d = devices; d; d = d->next) {
1243            if (android_name_to_log_id(d->device) == log_msg.id()) {
1244                break;
1245            }
1246        }
1247        if (!d) {
1248            g_devCount = 2; // set to Multiple
1249            d = &unexpected;
1250            d->binary = log_msg.id() == LOG_ID_EVENTS;
1251        }
1252
1253        if (dev != d) {
1254            dev = d;
1255            maybePrintStart(dev, printDividers);
1256        }
1257        if (g_printBinary) {
1258            printBinary(&log_msg);
1259        } else {
1260            processBuffer(dev, &log_msg);
1261        }
1262    }
1263
1264    android_logger_list_free(logger_list);
1265
1266    return EXIT_SUCCESS;
1267}
1268