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 
44 static AndroidLogFormat * g_logformat;
45 
46 /* logd prefixes records with a length field */
47 #define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t)
48 
49 struct 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 
log_device_tlog_device_t58     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 
68 namespace android {
69 
70 /* Global Variables */
71 
72 static const char * g_outputFileName;
73 // 0 means "no log rotation"
74 static size_t g_logRotateSizeKBytes;
75 // 0 means "unbounded"
76 static size_t g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS;
77 static int g_outFD = -1;
78 static size_t g_outByteCount;
79 static int g_printBinary;
80 static int g_devCount;                              // >1 means multiple
81 static pcrecpp::RE* g_regex;
82 // 0 means "infinite"
83 static size_t g_maxCount;
84 static size_t g_printCount;
85 static 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 
openLogFile(const char * pathname)90 static int openLogFile (const char *pathname)
91 {
92     return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
93 }
94 
rotateLogs()95 static 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 
printBinary(struct log_msg * buf)147 void printBinary(struct log_msg *buf)
148 {
149     size_t size = buf->len();
150 
151     TEMP_FAILURE_RETRY(write(g_outFD, buf, size));
152 }
153 
regexOk(const AndroidLogEntry & entry)154 static 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 
processBuffer(log_device_t * dev,struct log_msg * buf)165 static 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 
214 error:
215     //fprintf (stderr, "Error processing record\n");
216     return;
217 }
218 
maybePrintStart(log_device_t * dev,bool printDividers)219 static 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 
setupOutput()234 static 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 
show_help(const char * cmd)276 static 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 
setLogFormat(const char * formatString)358 static 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 
372 static const char multipliers[][2] = {
373     { "" },
374     { "K" },
375     { "M" },
376     { "G" }
377 };
378 
value_of_size(unsigned long value)379 static 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 
multiplier_of_size(unsigned long value)387 static 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*/
getSizeTArg(char * ptr,size_t * val,size_t min=0,size_t max=SIZE_MAX)397 static 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 
logcat_panic(bool showHelp,const char * fmt,...)420 static 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 
parseTime(log_time & t,const char * cp)434 static 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
lastLogTime(char * outputFileName)448 static 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 
main(int argc,char ** argv)541 int 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