• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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. Equivalent to filterspec '*:S'\n"
282                     "  -f <file>, --file=<file>               Log to file. Default is stdout\n"
283                     "  -r <kbytes>, --rotate-kbytes=<kbytes>\n"
284                     "                  Rotate log every kbytes. Requires -f option\n"
285                     "  -n <count>, --rotate-count=<count>\n"
286                     "                  Sets max number of rotated logs to <count>, default 4\n"
287                     "  -v <format>, --format=<format>\n"
288                     "                  Sets the log print format, where <format> is:\n"
289                     "                    brief color epoch long monotonic printable process raw\n"
290                     "                    tag thread threadtime time uid usec UTC year zone\n"
291                     "  -D, --dividers  Print dividers between each log buffer\n"
292                     "  -c, --clear     Clear (flush) the entire log and exit\n"
293                     "                  if Log to File specified, clear fileset instead\n"
294                     "  -d              Dump the log and then exit (don't block)\n"
295                     "  -e <expr>, --regex=<expr>\n"
296                     "                  Only print lines where the log message matches <expr>\n"
297                     "                  where <expr> is a regular expression\n"
298                     // Leave --head undocumented as alias for -m
299                     "  -m <count>, --max-count=<count>\n"
300                     "                  Quit after printing <count> lines. This is meant to be\n"
301                     "                  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                     // Leave --tail undocumented as alias for -t
305                     "  -t <count>      Print only the most recent <count> lines (implies -d)\n"
306                     "  -t '<time>'     Print most recent lines since specified time (implies -d)\n"
307                     "  -T <count>      Print only the most recent <count> lines (does not imply -d)\n"
308                     "  -T '<time>'     Print most recent lines since specified time (not imply -d)\n"
309                     "                  count is pure numerical, time is 'MM-DD hh:mm:ss.mmm...'\n"
310                     "                  'YYYY-MM-DD hh:mm:ss.mmm...' or 'sssss.mmm...' format\n"
311                     "  -g, --buffer-size                      Get the size of the ring buffer.\n"
312                     "  -G <size>, --buffer-size=<size>\n"
313                     "                  Set size of log ring buffer, may suffix with K or M.\n"
314                     "  -L, -last       Dump logs from prior to last reboot\n"
315                     // Leave security (Device Owner only installations) and
316                     // kernel (userdebug and eng) buffers undocumented.
317                     "  -b <buffer>, --buffer=<buffer>         Request alternate ring buffer, 'main',\n"
318                     "                  'system', 'radio', 'events', 'crash', 'default' or 'all'.\n"
319                     "                  Multiple -b parameters or comma separated list of buffers are\n"
320                     "                  allowed. Buffers interleaved. Default -b main,system,crash.\n"
321                     "  -B, --binary    Output the log in binary.\n"
322                     "  -S, --statistics                       Output statistics.\n"
323                     "  -p, --prune     Print prune white and ~black list. Service is specified as\n"
324                     "                  UID, UID/PID or /PID. Weighed for quicker pruning if prefix\n"
325                     "                  with ~, otherwise weighed for longevity if unadorned. All\n"
326                     "                  other pruning activity is oldest first. Special case ~!\n"
327                     "                  represents an automatic quicker pruning for the noisiest\n"
328                     "                  UID as determined by the current statistics.\n"
329                     "  -P '<list> ...', --prune='<list> ...'\n"
330                     "                  Set prune white and ~black list, using same format as\n"
331                     "                  listed above. Must be quoted.\n"
332                     "  --pid=<pid>     Only prints logs from the given pid.\n"
333                     // Check ANDROID_LOG_WRAP_DEFAULT_TIMEOUT value for match to 2 hours
334                     "  --wrap          Sleep for 2 hours or when buffer about to wrap whichever\n"
335                     "                  comes first. Improves efficiency of polling by providing\n"
336                     "                  an about-to-wrap wakeup.\n");
337 
338     fprintf(stderr,"\nfilterspecs are a series of \n"
339                    "  <tag>[:priority]\n\n"
340                    "where <tag> is a log component tag (or * for all) and priority is:\n"
341                    "  V    Verbose (default for <tag>)\n"
342                    "  D    Debug (default for '*')\n"
343                    "  I    Info\n"
344                    "  W    Warn\n"
345                    "  E    Error\n"
346                    "  F    Fatal\n"
347                    "  S    Silent (suppress all output)\n"
348                    "\n'*' by itself means '*:D' and <tag> by itself means <tag>:V.\n"
349                    "If no '*' filterspec or -s on command line, all filter defaults to '*:V'.\n"
350                    "eg: '*:S <tag>' prints only <tag>, '<tag>:S' suppresses all <tag> log messages.\n"
351                    "\nIf not specified on the command line, filterspec is set from ANDROID_LOG_TAGS.\n"
352                    "\nIf not specified with -v on command line, format is set from ANDROID_PRINTF_LOG\n"
353                    "or defaults to \"threadtime\"\n\n");
354 }
355 
setLogFormat(const char * formatString)356 static int setLogFormat(const char * formatString)
357 {
358     static AndroidLogPrintFormat format;
359 
360     format = android_log_formatFromString(formatString);
361 
362     if (format == FORMAT_OFF) {
363         // FORMAT_OFF means invalid string
364         return -1;
365     }
366 
367     return android_log_setPrintFormat(g_logformat, format);
368 }
369 
370 static const char multipliers[][2] = {
371     { "" },
372     { "K" },
373     { "M" },
374     { "G" }
375 };
376 
value_of_size(unsigned long value)377 static unsigned long value_of_size(unsigned long value)
378 {
379     for (unsigned i = 0;
380             (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024);
381             value /= 1024, ++i) ;
382     return value;
383 }
384 
multiplier_of_size(unsigned long value)385 static const char *multiplier_of_size(unsigned long value)
386 {
387     unsigned i;
388     for (i = 0;
389             (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024);
390             value /= 1024, ++i) ;
391     return multipliers[i];
392 }
393 
394 /*String to unsigned int, returns -1 if it fails*/
getSizeTArg(char * ptr,size_t * val,size_t min=0,size_t max=SIZE_MAX)395 static bool getSizeTArg(char *ptr, size_t *val, size_t min = 0,
396                         size_t max = SIZE_MAX)
397 {
398     if (!ptr) {
399         return false;
400     }
401 
402     char *endp;
403     errno = 0;
404     size_t ret = (size_t)strtoll(ptr, &endp, 0);
405 
406     if (endp[0] || errno) {
407         return false;
408     }
409 
410     if ((ret > max) || (ret < min)) {
411         return false;
412     }
413 
414     *val = ret;
415     return true;
416 }
417 
logcat_panic(bool showHelp,const char * fmt,...)418 static void logcat_panic(bool showHelp, const char *fmt, ...)
419 {
420     va_list  args;
421     va_start(args, fmt);
422     vfprintf(stderr, fmt,  args);
423     va_end(args);
424 
425     if (showHelp) {
426        show_help(getprogname());
427     }
428 
429     exit(EXIT_FAILURE);
430 }
431 
parseTime(log_time & t,const char * cp)432 static char *parseTime(log_time &t, const char *cp) {
433 
434     char *ep = t.strptime(cp, "%m-%d %H:%M:%S.%q");
435     if (ep) {
436         return ep;
437     }
438     ep = t.strptime(cp, "%Y-%m-%d %H:%M:%S.%q");
439     if (ep) {
440         return ep;
441     }
442     return t.strptime(cp, "%s.%q");
443 }
444 
445 // Find last logged line in gestalt of all matching existing output files
lastLogTime(char * outputFileName)446 static log_time lastLogTime(char *outputFileName) {
447     log_time retval(log_time::EPOCH);
448     if (!outputFileName) {
449         return retval;
450     }
451 
452     std::string directory;
453     char *file = strrchr(outputFileName, '/');
454     if (!file) {
455         directory = ".";
456         file = outputFileName;
457     } else {
458         *file = '\0';
459         directory = outputFileName;
460         *file = '/';
461         ++file;
462     }
463 
464     std::unique_ptr<DIR, int(*)(DIR*)>
465             dir(opendir(directory.c_str()), closedir);
466     if (!dir.get()) {
467         return retval;
468     }
469 
470     clockid_t clock_type = android_log_clockid();
471     log_time now(clock_type);
472     bool monotonic = clock_type == CLOCK_MONOTONIC;
473 
474     size_t len = strlen(file);
475     log_time modulo(0, NS_PER_SEC);
476     struct dirent *dp;
477 
478     while ((dp = readdir(dir.get())) != NULL) {
479         if ((dp->d_type != DT_REG)
480                 // If we are using realtime, check all files that match the
481                 // basename for latest time. If we are using monotonic time
482                 // then only check the main file because time cycles on
483                 // every reboot.
484                 || strncmp(dp->d_name, file, len + monotonic)
485                 || (dp->d_name[len]
486                     && ((dp->d_name[len] != '.')
487                         || !isdigit(dp->d_name[len+1])))) {
488             continue;
489         }
490 
491         std::string file_name = directory;
492         file_name += "/";
493         file_name += dp->d_name;
494         std::string file;
495         if (!android::base::ReadFileToString(file_name, &file)) {
496             continue;
497         }
498 
499         bool found = false;
500         for (const auto& line : android::base::Split(file, "\n")) {
501             log_time t(log_time::EPOCH);
502             char *ep = parseTime(t, line.c_str());
503             if (!ep || (*ep != ' ')) {
504                 continue;
505             }
506             // determine the time precision of the logs (eg: msec or usec)
507             for (unsigned long mod = 1UL; mod < modulo.tv_nsec; mod *= 10) {
508                 if (t.tv_nsec % (mod * 10)) {
509                     modulo.tv_nsec = mod;
510                     break;
511                 }
512             }
513             // We filter any times later than current as we may not have the
514             // year stored with each log entry. Also, since it is possible for
515             // entries to be recorded out of order (very rare) we select the
516             // maximum we find just in case.
517             if ((t < now) && (t > retval)) {
518                 retval = t;
519                 found = true;
520             }
521         }
522         // We count on the basename file to be the definitive end, so stop here.
523         if (!dp->d_name[len] && found) {
524             break;
525         }
526     }
527     if (retval == log_time::EPOCH) {
528         return retval;
529     }
530     // tail_time prints matching or higher, round up by the modulo to prevent
531     // a replay of the last entry we have just checked.
532     retval += modulo;
533     return retval;
534 }
535 
536 } /* namespace android */
537 
538 
main(int argc,char ** argv)539 int main(int argc, char **argv)
540 {
541     using namespace android;
542     int err;
543     int hasSetLogFormat = 0;
544     int clearLog = 0;
545     int getLogSize = 0;
546     unsigned long setLogSize = 0;
547     int getPruneList = 0;
548     char *setPruneList = NULL;
549     int printStatistics = 0;
550     int mode = ANDROID_LOG_RDONLY;
551     const char *forceFilters = NULL;
552     log_device_t* devices = NULL;
553     log_device_t* dev;
554     bool printDividers = false;
555     struct logger_list *logger_list;
556     size_t tail_lines = 0;
557     log_time tail_time(log_time::EPOCH);
558     size_t pid = 0;
559     bool got_t = false;
560 
561     signal(SIGPIPE, exit);
562 
563     g_logformat = android_log_format_new();
564 
565     if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
566         show_help(argv[0]);
567         return EXIT_SUCCESS;
568     }
569 
570     for (;;) {
571         int ret;
572 
573         int option_index = 0;
574         // list of long-argument only strings for later comparison
575         static const char pid_str[] = "pid";
576         static const char wrap_str[] = "wrap";
577         static const char print_str[] = "print";
578         static const struct option long_options[] = {
579           { "binary",        no_argument,       NULL,   'B' },
580           { "buffer",        required_argument, NULL,   'b' },
581           { "buffer-size",   optional_argument, NULL,   'g' },
582           { "clear",         no_argument,       NULL,   'c' },
583           { "dividers",      no_argument,       NULL,   'D' },
584           { "file",          required_argument, NULL,   'f' },
585           { "format",        required_argument, NULL,   'v' },
586           // hidden and undocumented reserved alias for --regex
587           { "grep",          required_argument, NULL,   'e' },
588           // hidden and undocumented reserved alias for --max-count
589           { "head",          required_argument, NULL,   'm' },
590           { "last",          no_argument,       NULL,   'L' },
591           { "max-count",     required_argument, NULL,   'm' },
592           { pid_str,         required_argument, NULL,   0 },
593           { print_str,       no_argument,       NULL,   0 },
594           { "prune",         optional_argument, NULL,   'p' },
595           { "regex",         required_argument, NULL,   'e' },
596           { "rotate-count",  required_argument, NULL,   'n' },
597           { "rotate-kbytes", required_argument, NULL,   'r' },
598           { "statistics",    no_argument,       NULL,   'S' },
599           // hidden and undocumented reserved alias for -t
600           { "tail",          required_argument, NULL,   't' },
601           // support, but ignore and do not document, the optional argument
602           { wrap_str,        optional_argument, NULL,   0 },
603           { NULL,            0,                 NULL,   0 }
604         };
605 
606         ret = getopt_long(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:",
607                           long_options, &option_index);
608 
609         if (ret < 0) {
610             break;
611         }
612 
613         switch (ret) {
614             case 0:
615                 // One of the long options
616                 if (long_options[option_index].name == pid_str) {
617                     // ToDo: determine runtime PID_MAX?
618                     if (!getSizeTArg(optarg, &pid, 1)) {
619                         logcat_panic(true, "%s %s out of range\n",
620                                      long_options[option_index].name, optarg);
621                     }
622                     break;
623                 }
624                 if (long_options[option_index].name == wrap_str) {
625                     mode |= ANDROID_LOG_WRAP |
626                             ANDROID_LOG_RDONLY |
627                             ANDROID_LOG_NONBLOCK;
628                     // ToDo: implement API that supports setting a wrap timeout
629                     size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
630                     if (optarg && !getSizeTArg(optarg, &dummy, 1)) {
631                         logcat_panic(true, "%s %s out of range\n",
632                                      long_options[option_index].name, optarg);
633                     }
634                     if (dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) {
635                         fprintf(stderr,
636                                 "WARNING: %s %u seconds, ignoring %zu\n",
637                                 long_options[option_index].name,
638                                 ANDROID_LOG_WRAP_DEFAULT_TIMEOUT, dummy);
639                     }
640                     break;
641                 }
642                 if (long_options[option_index].name == print_str) {
643                     g_printItAnyways = true;
644                     break;
645                 }
646             break;
647 
648             case 's':
649                 // default to all silent
650                 android_log_addFilterRule(g_logformat, "*:s");
651             break;
652 
653             case 'c':
654                 clearLog = 1;
655                 mode |= ANDROID_LOG_WRONLY;
656             break;
657 
658             case 'L':
659                 mode |= ANDROID_LOG_PSTORE;
660             break;
661 
662             case 'd':
663                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
664             break;
665 
666             case 't':
667                 got_t = true;
668                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
669                 /* FALLTHRU */
670             case 'T':
671                 if (strspn(optarg, "0123456789") != strlen(optarg)) {
672                     char *cp = parseTime(tail_time, optarg);
673                     if (!cp) {
674                         logcat_panic(false, "-%c \"%s\" not in time format\n",
675                                      ret, optarg);
676                     }
677                     if (*cp) {
678                         char c = *cp;
679                         *cp = '\0';
680                         fprintf(stderr,
681                                 "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
682                                 ret, optarg, c, cp + 1);
683                         *cp = c;
684                     }
685                 } else {
686                     if (!getSizeTArg(optarg, &tail_lines, 1)) {
687                         fprintf(stderr,
688                                 "WARNING: -%c %s invalid, setting to 1\n",
689                                 ret, optarg);
690                         tail_lines = 1;
691                     }
692                 }
693             break;
694 
695             case 'D':
696                 printDividers = true;
697             break;
698 
699             case 'e':
700                 g_regex = new pcrecpp::RE(optarg);
701             break;
702 
703             case 'm': {
704                 char *end = NULL;
705                 if (!getSizeTArg(optarg, &g_maxCount)) {
706                     logcat_panic(false, "-%c \"%s\" isn't an "
707                                  "integer greater than zero\n", ret, optarg);
708                 }
709             }
710             break;
711 
712             case 'g':
713                 if (!optarg) {
714                     getLogSize = 1;
715                     break;
716                 }
717                 // FALLTHRU
718 
719             case 'G': {
720                 char *cp;
721                 if (strtoll(optarg, &cp, 0) > 0) {
722                     setLogSize = strtoll(optarg, &cp, 0);
723                 } else {
724                     setLogSize = 0;
725                 }
726 
727                 switch(*cp) {
728                 case 'g':
729                 case 'G':
730                     setLogSize *= 1024;
731                 /* FALLTHRU */
732                 case 'm':
733                 case 'M':
734                     setLogSize *= 1024;
735                 /* FALLTHRU */
736                 case 'k':
737                 case 'K':
738                     setLogSize *= 1024;
739                 /* FALLTHRU */
740                 case '\0':
741                 break;
742 
743                 default:
744                     setLogSize = 0;
745                 }
746 
747                 if (!setLogSize) {
748                     fprintf(stderr, "ERROR: -G <num><multiplier>\n");
749                     return EXIT_FAILURE;
750                 }
751             }
752             break;
753 
754             case 'p':
755                 if (!optarg) {
756                     getPruneList = 1;
757                     break;
758                 }
759                 // FALLTHRU
760 
761             case 'P':
762                 setPruneList = optarg;
763             break;
764 
765             case 'b': {
766                 unsigned idMask = 0;
767                 while ((optarg = strtok(optarg, ",:; \t\n\r\f")) != NULL) {
768                     if (strcmp(optarg, "default") == 0) {
769                         idMask |= (1 << LOG_ID_MAIN) |
770                                   (1 << LOG_ID_SYSTEM) |
771                                   (1 << LOG_ID_CRASH);
772                     } else if (strcmp(optarg, "all") == 0) {
773                         idMask = (unsigned)-1;
774                     } else {
775                         log_id_t log_id = android_name_to_log_id(optarg);
776                         const char *name = android_log_id_to_name(log_id);
777 
778                         if (strcmp(name, optarg) != 0) {
779                             logcat_panic(true, "unknown buffer %s\n", optarg);
780                         }
781                         idMask |= (1 << log_id);
782                     }
783                     optarg = NULL;
784                 }
785 
786                 for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
787                     const char *name = android_log_id_to_name((log_id_t)i);
788                     log_id_t log_id = android_name_to_log_id(name);
789 
790                     if (log_id != (log_id_t)i) {
791                         continue;
792                     }
793                     if ((idMask & (1 << i)) == 0) {
794                         continue;
795                     }
796 
797                     bool found = false;
798                     for (dev = devices; dev; dev = dev->next) {
799                         if (!strcmp(name, dev->device)) {
800                             found = true;
801                             break;
802                         }
803                         if (!dev->next) {
804                             break;
805                         }
806                     }
807                     if (found) {
808                         continue;
809                     }
810 
811                     bool binary = !strcmp(name, "events") ||
812                                   !strcmp(name, "security");
813                     log_device_t* d = new log_device_t(name, binary);
814 
815                     if (dev) {
816                         dev->next = d;
817                         dev = d;
818                     } else {
819                         devices = dev = d;
820                     }
821                     g_devCount++;
822                 }
823             }
824             break;
825 
826             case 'B':
827                 g_printBinary = 1;
828             break;
829 
830             case 'f':
831                 if ((tail_time == log_time::EPOCH) && (tail_lines == 0)) {
832                     tail_time = lastLogTime(optarg);
833                 }
834                 // redirect output to a file
835                 g_outputFileName = optarg;
836             break;
837 
838             case 'r':
839                 if (!getSizeTArg(optarg, &g_logRotateSizeKBytes, 1)) {
840                     logcat_panic(true, "Invalid parameter %s to -r\n", optarg);
841                 }
842             break;
843 
844             case 'n':
845                 if (!getSizeTArg(optarg, &g_maxRotatedLogs, 1)) {
846                     logcat_panic(true, "Invalid parameter %s to -n\n", optarg);
847                 }
848             break;
849 
850             case 'v':
851                 err = setLogFormat (optarg);
852                 if (err < 0) {
853                     logcat_panic(true, "Invalid parameter %s to -v\n", optarg);
854                 }
855                 hasSetLogFormat |= err;
856             break;
857 
858             case 'Q':
859                 /* this is a *hidden* option used to start a version of logcat                 */
860                 /* in an emulated device only. it basically looks for androidboot.logcat=      */
861                 /* on the kernel command line. If something is found, it extracts a log filter */
862                 /* and uses it to run the program. If nothing is found, the program should     */
863                 /* quit immediately                                                            */
864 #define  KERNEL_OPTION  "androidboot.logcat="
865 #define  CONSOLE_OPTION "androidboot.console="
866                 {
867                     int          fd;
868                     char*        logcat;
869                     char*        console;
870                     int          force_exit = 1;
871                     static char  cmdline[1024];
872 
873                     fd = open("/proc/cmdline", O_RDONLY);
874                     if (fd >= 0) {
875                         int  n = read(fd, cmdline, sizeof(cmdline)-1 );
876                         if (n < 0) n = 0;
877                         cmdline[n] = 0;
878                         close(fd);
879                     } else {
880                         cmdline[0] = 0;
881                     }
882 
883                     logcat  = strstr( cmdline, KERNEL_OPTION );
884                     console = strstr( cmdline, CONSOLE_OPTION );
885                     if (logcat != NULL) {
886                         char*  p = logcat + sizeof(KERNEL_OPTION)-1;;
887                         char*  q = strpbrk( p, " \t\n\r" );;
888 
889                         if (q != NULL)
890                             *q = 0;
891 
892                         forceFilters = p;
893                         force_exit   = 0;
894                     }
895                     /* if nothing found or invalid filters, exit quietly */
896                     if (force_exit) {
897                         return EXIT_SUCCESS;
898                     }
899 
900                     /* redirect our output to the emulator console */
901                     if (console) {
902                         char*  p = console + sizeof(CONSOLE_OPTION)-1;
903                         char*  q = strpbrk( p, " \t\n\r" );
904                         char   devname[64];
905                         int    len;
906 
907                         if (q != NULL) {
908                             len = q - p;
909                         } else
910                             len = strlen(p);
911 
912                         len = snprintf( devname, sizeof(devname), "/dev/%.*s", len, p );
913                         fprintf(stderr, "logcat using %s (%d)\n", devname, len);
914                         if (len < (int)sizeof(devname)) {
915                             fd = open( devname, O_WRONLY );
916                             if (fd >= 0) {
917                                 dup2(fd, 1);
918                                 dup2(fd, 2);
919                                 close(fd);
920                             }
921                         }
922                     }
923                 }
924                 break;
925 
926             case 'S':
927                 printStatistics = 1;
928                 break;
929 
930             case ':':
931                 logcat_panic(true, "Option -%c needs an argument\n", optopt);
932                 break;
933 
934             default:
935                 logcat_panic(true, "Unrecognized Option %c\n", optopt);
936                 break;
937         }
938     }
939 
940     if (g_maxCount && got_t) {
941         logcat_panic(true, "Cannot use -m (--max-count) and -t together\n");
942     }
943     if (g_printItAnyways && (!g_regex || !g_maxCount)) {
944         // One day it would be nice if --print -v color and --regex <expr>
945         // could play with each other and show regex highlighted content.
946         fprintf(stderr, "WARNING: "
947                             "--print ignored, to be used in combination with\n"
948                         "         "
949                             "--regex <expr> and --max-count <N>\n");
950         g_printItAnyways = false;
951     }
952 
953     if (!devices) {
954         dev = devices = new log_device_t("main", false);
955         g_devCount = 1;
956         if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
957             dev = dev->next = new log_device_t("system", false);
958             g_devCount++;
959         }
960         if (android_name_to_log_id("crash") == LOG_ID_CRASH) {
961             dev = dev->next = new log_device_t("crash", false);
962             g_devCount++;
963         }
964     }
965 
966     if (g_logRotateSizeKBytes != 0 && g_outputFileName == NULL) {
967         logcat_panic(true, "-r requires -f as well\n");
968     }
969 
970     setupOutput();
971 
972     if (hasSetLogFormat == 0) {
973         const char* logFormat = getenv("ANDROID_PRINTF_LOG");
974 
975         if (logFormat != NULL) {
976             err = setLogFormat(logFormat);
977             if (err < 0) {
978                 fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n",
979                                     logFormat);
980             }
981         } else {
982             setLogFormat("threadtime");
983         }
984     }
985 
986     if (forceFilters) {
987         err = android_log_addFilterString(g_logformat, forceFilters);
988         if (err < 0) {
989             logcat_panic(false, "Invalid filter expression in logcat args\n");
990         }
991     } else if (argc == optind) {
992         // Add from environment variable
993         char *env_tags_orig = getenv("ANDROID_LOG_TAGS");
994 
995         if (env_tags_orig != NULL) {
996             err = android_log_addFilterString(g_logformat, env_tags_orig);
997 
998             if (err < 0) {
999                 logcat_panic(true,
1000                             "Invalid filter expression in ANDROID_LOG_TAGS\n");
1001             }
1002         }
1003     } else {
1004         // Add from commandline
1005         for (int i = optind ; i < argc ; i++) {
1006             err = android_log_addFilterString(g_logformat, argv[i]);
1007 
1008             if (err < 0) {
1009                 logcat_panic(true, "Invalid filter expression '%s'\n", argv[i]);
1010             }
1011         }
1012     }
1013 
1014     dev = devices;
1015     if (tail_time != log_time::EPOCH) {
1016         logger_list = android_logger_list_alloc_time(mode, tail_time, pid);
1017     } else {
1018         logger_list = android_logger_list_alloc(mode, tail_lines, pid);
1019     }
1020     const char *openDeviceFail = NULL;
1021     const char *clearFail = NULL;
1022     const char *setSizeFail = NULL;
1023     const char *getSizeFail = NULL;
1024     // We have three orthogonal actions below to clear, set log size and
1025     // get log size. All sharing the same iteration loop.
1026     while (dev) {
1027         dev->logger_list = logger_list;
1028         dev->logger = android_logger_open(logger_list,
1029                                           android_name_to_log_id(dev->device));
1030         if (!dev->logger) {
1031             openDeviceFail = openDeviceFail ?: dev->device;
1032             dev = dev->next;
1033             continue;
1034         }
1035 
1036         if (clearLog) {
1037             if (g_outputFileName) {
1038                 int maxRotationCountDigits =
1039                     (g_maxRotatedLogs > 0) ? (int) (floor(log10(g_maxRotatedLogs) + 1)) : 0;
1040 
1041                 for (int i = g_maxRotatedLogs ; i >= 0 ; --i) {
1042                     char *file;
1043 
1044                     if (i == 0) {
1045                         asprintf(&file, "%s", g_outputFileName);
1046                     } else {
1047                         asprintf(&file, "%s.%.*d", g_outputFileName, maxRotationCountDigits, i);
1048                     }
1049 
1050                     if (!file) {
1051                         perror("while clearing log files");
1052                         clearFail = clearFail ?: dev->device;
1053                         break;
1054                     }
1055 
1056                     err = unlink(file);
1057 
1058                     if (err < 0 && errno != ENOENT && clearFail == NULL) {
1059                         perror("while clearing log files");
1060                         clearFail = dev->device;
1061                     }
1062 
1063                     free(file);
1064                 }
1065             } else if (android_logger_clear(dev->logger)) {
1066                 clearFail = clearFail ?: dev->device;
1067             }
1068         }
1069 
1070         if (setLogSize) {
1071             if (android_logger_set_log_size(dev->logger, setLogSize)) {
1072                 setSizeFail = setSizeFail ?: dev->device;
1073             }
1074         }
1075 
1076         if (getLogSize) {
1077             long size = android_logger_get_log_size(dev->logger);
1078             long readable = android_logger_get_log_readable_size(dev->logger);
1079 
1080             if ((size < 0) || (readable < 0)) {
1081                 getSizeFail = getSizeFail ?: dev->device;
1082             } else {
1083                 printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
1084                        "max entry is %db, max payload is %db\n", dev->device,
1085                        value_of_size(size), multiplier_of_size(size),
1086                        value_of_size(readable), multiplier_of_size(readable),
1087                        (int) LOGGER_ENTRY_MAX_LEN,
1088                        (int) LOGGER_ENTRY_MAX_PAYLOAD);
1089             }
1090         }
1091 
1092         dev = dev->next;
1093     }
1094     // report any errors in the above loop and exit
1095     if (openDeviceFail) {
1096         logcat_panic(false, "Unable to open log device '%s'\n", openDeviceFail);
1097     }
1098     if (clearFail) {
1099         logcat_panic(false, "failed to clear the '%s' log\n", clearFail);
1100     }
1101     if (setSizeFail) {
1102         logcat_panic(false, "failed to set the '%s' log size\n", setSizeFail);
1103     }
1104     if (getSizeFail) {
1105         logcat_panic(false, "failed to get the readable '%s' log size",
1106                      getSizeFail);
1107     }
1108 
1109     if (setPruneList) {
1110         size_t len = strlen(setPruneList);
1111         /*extra 32 bytes are needed by  android_logger_set_prune_list */
1112         size_t bLen = len + 32;
1113         char *buf = NULL;
1114         if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
1115             buf[len] = '\0';
1116             if (android_logger_set_prune_list(logger_list, buf, bLen)) {
1117                 logcat_panic(false, "failed to set the prune list");
1118             }
1119             free(buf);
1120         } else {
1121             logcat_panic(false, "failed to set the prune list (alloc)");
1122         }
1123     }
1124 
1125     if (printStatistics || getPruneList) {
1126         size_t len = 8192;
1127         char *buf;
1128 
1129         for (int retry = 32;
1130                 (retry >= 0) && ((buf = new char [len]));
1131                 delete [] buf, buf = NULL, --retry) {
1132             if (getPruneList) {
1133                 android_logger_get_prune_list(logger_list, buf, len);
1134             } else {
1135                 android_logger_get_statistics(logger_list, buf, len);
1136             }
1137             buf[len-1] = '\0';
1138             if (atol(buf) < 3) {
1139                 delete [] buf;
1140                 buf = NULL;
1141                 break;
1142             }
1143             size_t ret = atol(buf) + 1;
1144             if (ret <= len) {
1145                 len = ret;
1146                 break;
1147             }
1148             len = ret;
1149         }
1150 
1151         if (!buf) {
1152             logcat_panic(false, "failed to read data");
1153         }
1154 
1155         // remove trailing FF
1156         char *cp = buf + len - 1;
1157         *cp = '\0';
1158         bool truncated = *--cp != '\f';
1159         if (!truncated) {
1160             *cp = '\0';
1161         }
1162 
1163         // squash out the byte count
1164         cp = buf;
1165         if (!truncated) {
1166             while (isdigit(*cp)) {
1167                 ++cp;
1168             }
1169             if (*cp == '\n') {
1170                 ++cp;
1171             }
1172         }
1173 
1174         printf("%s", cp);
1175         delete [] buf;
1176         return EXIT_SUCCESS;
1177     }
1178 
1179 
1180     if (getLogSize) {
1181         return EXIT_SUCCESS;
1182     }
1183     if (setLogSize || setPruneList) {
1184         return EXIT_SUCCESS;
1185     }
1186     if (clearLog) {
1187         return EXIT_SUCCESS;
1188     }
1189 
1190     //LOG_EVENT_INT(10, 12345);
1191     //LOG_EVENT_LONG(11, 0x1122334455667788LL);
1192     //LOG_EVENT_STRING(0, "whassup, doc?");
1193 
1194     dev = NULL;
1195     log_device_t unexpected("unexpected", false);
1196 
1197     while (!g_maxCount || (g_printCount < g_maxCount)) {
1198         struct log_msg log_msg;
1199         log_device_t* d;
1200         int ret = android_logger_list_read(logger_list, &log_msg);
1201 
1202         if (ret == 0) {
1203             logcat_panic(false, "read: unexpected EOF!\n");
1204         }
1205 
1206         if (ret < 0) {
1207             if (ret == -EAGAIN) {
1208                 break;
1209             }
1210 
1211             if (ret == -EIO) {
1212                 logcat_panic(false, "read: unexpected EOF!\n");
1213             }
1214             if (ret == -EINVAL) {
1215                 logcat_panic(false, "read: unexpected length.\n");
1216             }
1217             logcat_panic(false, "logcat read failure");
1218         }
1219 
1220         for (d = devices; d; d = d->next) {
1221             if (android_name_to_log_id(d->device) == log_msg.id()) {
1222                 break;
1223             }
1224         }
1225         if (!d) {
1226             g_devCount = 2; // set to Multiple
1227             d = &unexpected;
1228             d->binary = log_msg.id() == LOG_ID_EVENTS;
1229         }
1230 
1231         if (dev != d) {
1232             dev = d;
1233             maybePrintStart(dev, printDividers);
1234         }
1235         if (g_printBinary) {
1236             printBinary(&log_msg);
1237         } else {
1238             processBuffer(dev, &log_msg);
1239         }
1240     }
1241 
1242     android_logger_list_free(logger_list);
1243 
1244     return EXIT_SUCCESS;
1245 }
1246