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 <math.h>
10 #include <sched.h>
11 #include <signal.h>
12 #include <stdarg.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/cdefs.h>
17 #include <sys/resource.h>
18 #include <sys/socket.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <time.h>
22 #include <unistd.h>
23 
24 #include <memory>
25 #include <string>
26 
27 #include <base/file.h>
28 #include <base/strings.h>
29 #include <cutils/sched_policy.h>
30 #include <cutils/sockets.h>
31 #include <log/event_tag_map.h>
32 #include <log/log.h>
33 #include <log/log_read.h>
34 #include <log/logd.h>
35 #include <log/logger.h>
36 #include <log/logprint.h>
37 #include <utils/threads.h>
38 
39 #define DEFAULT_MAX_ROTATED_LOGS 4
40 
41 static AndroidLogFormat * g_logformat;
42 
43 /* logd prefixes records with a length field */
44 #define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t)
45 
46 struct log_device_t {
47     const char* device;
48     bool binary;
49     struct logger *logger;
50     struct logger_list *logger_list;
51     bool printed;
52 
53     log_device_t* next;
54 
log_device_tlog_device_t55     log_device_t(const char* d, bool b) {
56         device = d;
57         binary = b;
58         next = NULL;
59         printed = false;
60         logger = NULL;
61         logger_list = NULL;
62     }
63 };
64 
65 namespace android {
66 
67 /* Global Variables */
68 
69 static const char * g_outputFileName = NULL;
70 // 0 means "no log rotation"
71 static size_t g_logRotateSizeKBytes = 0;
72 // 0 means "unbounded"
73 static size_t g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS;
74 static int g_outFD = -1;
75 static size_t g_outByteCount = 0;
76 static int g_printBinary = 0;
77 static int g_devCount = 0;                              // >1 means multiple
78 
79 __noreturn static void logcat_panic(bool showHelp, const char *fmt, ...) __printflike(2,3);
80 
openLogFile(const char * pathname)81 static int openLogFile (const char *pathname)
82 {
83     return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
84 }
85 
rotateLogs()86 static void rotateLogs()
87 {
88     int err;
89 
90     // Can't rotate logs if we're not outputting to a file
91     if (g_outputFileName == NULL) {
92         return;
93     }
94 
95     close(g_outFD);
96 
97     // Compute the maximum number of digits needed to count up to g_maxRotatedLogs in decimal.
98     // eg: g_maxRotatedLogs == 30 -> log10(30) == 1.477 -> maxRotationCountDigits == 2
99     int maxRotationCountDigits =
100             (g_maxRotatedLogs > 0) ? (int) (floor(log10(g_maxRotatedLogs) + 1)) : 0;
101 
102     for (int i = g_maxRotatedLogs ; i > 0 ; i--) {
103         char *file0, *file1;
104 
105         asprintf(&file1, "%s.%.*d", g_outputFileName, maxRotationCountDigits, i);
106 
107         if (i - 1 == 0) {
108             asprintf(&file0, "%s", g_outputFileName);
109         } else {
110             asprintf(&file0, "%s.%.*d", g_outputFileName, maxRotationCountDigits, i - 1);
111         }
112 
113         if (!file0 || !file1) {
114             perror("while rotating log files");
115             break;
116         }
117 
118         err = rename(file0, file1);
119 
120         if (err < 0 && errno != ENOENT) {
121             perror("while rotating log files");
122         }
123 
124         free(file1);
125         free(file0);
126     }
127 
128     g_outFD = openLogFile(g_outputFileName);
129 
130     if (g_outFD < 0) {
131         logcat_panic(false, "couldn't open output file");
132     }
133 
134     g_outByteCount = 0;
135 
136 }
137 
printBinary(struct log_msg * buf)138 void printBinary(struct log_msg *buf)
139 {
140     size_t size = buf->len();
141 
142     TEMP_FAILURE_RETRY(write(g_outFD, buf, size));
143 }
144 
processBuffer(log_device_t * dev,struct log_msg * buf)145 static void processBuffer(log_device_t* dev, struct log_msg *buf)
146 {
147     int bytesWritten = 0;
148     int err;
149     AndroidLogEntry entry;
150     char binaryMsgBuf[1024];
151 
152     if (dev->binary) {
153         static bool hasOpenedEventTagMap = false;
154         static EventTagMap *eventTagMap = NULL;
155 
156         if (!eventTagMap && !hasOpenedEventTagMap) {
157             eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE);
158             hasOpenedEventTagMap = true;
159         }
160         err = android_log_processBinaryLogBuffer(&buf->entry_v1, &entry,
161                                                  eventTagMap,
162                                                  binaryMsgBuf,
163                                                  sizeof(binaryMsgBuf));
164         //printf(">>> pri=%d len=%d msg='%s'\n",
165         //    entry.priority, entry.messageLen, entry.message);
166     } else {
167         err = android_log_processLogBuffer(&buf->entry_v1, &entry);
168     }
169     if (err < 0) {
170         goto error;
171     }
172 
173     if (android_log_shouldPrintLine(g_logformat, entry.tag, entry.priority)) {
174         bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry);
175 
176         if (bytesWritten < 0) {
177             logcat_panic(false, "output error");
178         }
179     }
180 
181     g_outByteCount += bytesWritten;
182 
183     if (g_logRotateSizeKBytes > 0
184         && (g_outByteCount / 1024) >= g_logRotateSizeKBytes
185     ) {
186         rotateLogs();
187     }
188 
189 error:
190     //fprintf (stderr, "Error processing record\n");
191     return;
192 }
193 
maybePrintStart(log_device_t * dev,bool printDividers)194 static void maybePrintStart(log_device_t* dev, bool printDividers) {
195     if (!dev->printed || printDividers) {
196         if (g_devCount > 1 && !g_printBinary) {
197             char buf[1024];
198             snprintf(buf, sizeof(buf), "--------- %s %s\n",
199                      dev->printed ? "switch to" : "beginning of",
200                      dev->device);
201             if (write(g_outFD, buf, strlen(buf)) < 0) {
202                 logcat_panic(false, "output error");
203             }
204         }
205         dev->printed = true;
206     }
207 }
208 
setupOutput()209 static void setupOutput()
210 {
211 
212     if (g_outputFileName == NULL) {
213         g_outFD = STDOUT_FILENO;
214 
215     } else {
216         if (set_sched_policy(0, SP_BACKGROUND) < 0) {
217             fprintf(stderr, "failed to set background scheduling policy\n");
218         }
219 
220         struct sched_param param;
221         memset(&param, 0, sizeof(param));
222         if (sched_setscheduler((pid_t) 0, SCHED_BATCH, &param) < 0) {
223             fprintf(stderr, "failed to set to batch scheduler\n");
224         }
225 
226         if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
227             fprintf(stderr, "failed set to priority\n");
228         }
229 
230         g_outFD = openLogFile (g_outputFileName);
231 
232         if (g_outFD < 0) {
233             logcat_panic(false, "couldn't open output file");
234         }
235 
236         struct stat statbuf;
237         if (fstat(g_outFD, &statbuf) == -1) {
238             close(g_outFD);
239             logcat_panic(false, "couldn't get output file stat\n");
240         }
241 
242         if ((size_t) statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
243             close(g_outFD);
244             logcat_panic(false, "invalid output file stat\n");
245         }
246 
247         g_outByteCount = statbuf.st_size;
248     }
249 }
250 
show_help(const char * cmd)251 static void show_help(const char *cmd)
252 {
253     fprintf(stderr,"Usage: %s [options] [filterspecs]\n", cmd);
254 
255     fprintf(stderr, "options include:\n"
256                     "  -s              Set default filter to silent.\n"
257                     "                  Like specifying filterspec '*:S'\n"
258                     "  -f <filename>   Log to file. Default is stdout\n"
259                     "  -r <kbytes>     Rotate log every kbytes. Requires -f\n"
260                     "  -n <count>      Sets max number of rotated logs to <count>, default 4\n"
261                     "  -v <format>     Sets the log print format, where <format> is:\n\n"
262                     "                      brief color long printable process raw tag thread\n"
263                     "                      threadtime time usec\n\n"
264                     "  -D              print dividers between each log buffer\n"
265                     "  -c              clear (flush) the entire log and exit\n"
266                     "  -d              dump the log and then exit (don't block)\n"
267                     "  -t <count>      print only the most recent <count> lines (implies -d)\n"
268                     "  -t '<time>'     print most recent lines since specified time (implies -d)\n"
269                     "  -T <count>      print only the most recent <count> lines (does not imply -d)\n"
270                     "  -T '<time>'     print most recent lines since specified time (not imply -d)\n"
271                     "                  count is pure numerical, time is 'MM-DD hh:mm:ss.mmm'\n"
272                     "  -g              get the size of the log's ring buffer and exit\n"
273                     "  -L              dump logs from prior to last reboot\n"
274                     "  -b <buffer>     Request alternate ring buffer, 'main', 'system', 'radio',\n"
275                     "                  'events', 'crash' or 'all'. Multiple -b parameters are\n"
276                     "                  allowed and results are interleaved. The default is\n"
277                     "                  -b main -b system -b crash.\n"
278                     "  -B              output the log in binary.\n"
279                     "  -S              output statistics.\n"
280                     "  -G <size>       set size of log ring buffer, may suffix with K or M.\n"
281                     "  -p              print prune white and ~black list. Service is specified as\n"
282                     "                  UID, UID/PID or /PID. Weighed for quicker pruning if prefix\n"
283                     "                  with ~, otherwise weighed for longevity if unadorned. All\n"
284                     "                  other pruning activity is oldest first. Special case ~!\n"
285                     "                  represents an automatic quicker pruning for the noisiest\n"
286                     "                  UID as determined by the current statistics.\n"
287                     "  -P '<list> ...' set prune white and ~black list, using same format as\n"
288                     "                  printed above. Must be quoted.\n");
289 
290     fprintf(stderr,"\nfilterspecs are a series of \n"
291                    "  <tag>[:priority]\n\n"
292                    "where <tag> is a log component tag (or * for all) and priority is:\n"
293                    "  V    Verbose (default for <tag>)\n"
294                    "  D    Debug (default for '*')\n"
295                    "  I    Info\n"
296                    "  W    Warn\n"
297                    "  E    Error\n"
298                    "  F    Fatal\n"
299                    "  S    Silent (suppress all output)\n"
300                    "\n'*' by itself means '*:D' and <tag> by itself means <tag>:V.\n"
301                    "If no '*' filterspec or -s on command line, all filter defaults to '*:V'.\n"
302                    "eg: '*:S <tag>' prints only <tag>, '<tag>:S' suppresses all <tag> log messages.\n"
303                    "\nIf not specified on the command line, filterspec is set from ANDROID_LOG_TAGS.\n"
304                    "\nIf not specified with -v on command line, format is set from ANDROID_PRINTF_LOG\n"
305                    "or defaults to \"threadtime\"\n\n");
306 }
307 
setLogFormat(const char * formatString)308 static int setLogFormat(const char * formatString)
309 {
310     static AndroidLogPrintFormat format;
311 
312     format = android_log_formatFromString(formatString);
313 
314     if (format == FORMAT_OFF) {
315         // FORMAT_OFF means invalid string
316         return -1;
317     }
318 
319     return android_log_setPrintFormat(g_logformat, format);
320 }
321 
322 static const char multipliers[][2] = {
323     { "" },
324     { "K" },
325     { "M" },
326     { "G" }
327 };
328 
value_of_size(unsigned long value)329 static unsigned long value_of_size(unsigned long value)
330 {
331     for (unsigned i = 0;
332             (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024);
333             value /= 1024, ++i) ;
334     return value;
335 }
336 
multiplier_of_size(unsigned long value)337 static const char *multiplier_of_size(unsigned long value)
338 {
339     unsigned i;
340     for (i = 0;
341             (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024);
342             value /= 1024, ++i) ;
343     return multipliers[i];
344 }
345 
346 /*String to unsigned int, returns -1 if it fails*/
getSizeTArg(char * ptr,size_t * val,size_t min=0,size_t max=SIZE_MAX)347 static bool getSizeTArg(char *ptr, size_t *val, size_t min = 0,
348                         size_t max = SIZE_MAX)
349 {
350     char *endp;
351     errno = 0;
352     size_t ret = (size_t) strtoll(ptr, &endp, 0);
353 
354     if (endp[0] != '\0' || errno != 0 ) {
355         return false;
356     }
357 
358     if (ret >  max || ret <  min) {
359         return false;
360     }
361 
362     *val = ret;
363     return true;
364 }
365 
logcat_panic(bool showHelp,const char * fmt,...)366 static void logcat_panic(bool showHelp, const char *fmt, ...)
367 {
368     va_list  args;
369     va_start(args, fmt);
370     vfprintf(stderr, fmt,  args);
371     va_end(args);
372 
373     if (showHelp) {
374        show_help(getprogname());
375     }
376 
377     exit(EXIT_FAILURE);
378 }
379 
380 static const char g_defaultTimeFormat[] = "%m-%d %H:%M:%S.%q";
381 
382 // Find last logged line in gestalt of all matching existing output files
lastLogTime(char * outputFileName)383 static log_time lastLogTime(char *outputFileName) {
384     log_time retval(log_time::EPOCH);
385     if (!outputFileName) {
386         return retval;
387     }
388 
389     log_time now(CLOCK_REALTIME);
390 
391     std::string directory;
392     char *file = strrchr(outputFileName, '/');
393     if (!file) {
394         directory = ".";
395         file = outputFileName;
396     } else {
397         *file = '\0';
398         directory = outputFileName;
399         *file = '/';
400         ++file;
401     }
402     size_t len = strlen(file);
403     log_time modulo(0, NS_PER_SEC);
404     std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(directory.c_str()), closedir);
405     struct dirent *dp;
406     while ((dp = readdir(dir.get())) != NULL) {
407         if ((dp->d_type != DT_REG)
408                 || strncmp(dp->d_name, file, len)
409                 || (dp->d_name[len]
410                     && ((dp->d_name[len] != '.')
411                         || !isdigit(dp->d_name[len+1])))) {
412             continue;
413         }
414 
415         std::string file_name = directory;
416         file_name += "/";
417         file_name += dp->d_name;
418         std::string file;
419         if (!android::base::ReadFileToString(file_name, &file)) {
420             continue;
421         }
422 
423         bool found = false;
424         for (const auto& line : android::base::Split(file, "\n")) {
425             log_time t(log_time::EPOCH);
426             char *ep = t.strptime(line.c_str(), g_defaultTimeFormat);
427             if (!ep || (*ep != ' ')) {
428                 continue;
429             }
430             // determine the time precision of the logs (eg: msec or usec)
431             for (unsigned long mod = 1UL; mod < modulo.tv_nsec; mod *= 10) {
432                 if (t.tv_nsec % (mod * 10)) {
433                     modulo.tv_nsec = mod;
434                     break;
435                 }
436             }
437             // We filter any times later than current as we may not have the
438             // year stored with each log entry. Also, since it is possible for
439             // entries to be recorded out of order (very rare) we select the
440             // maximum we find just in case.
441             if ((t < now) && (t > retval)) {
442                 retval = t;
443                 found = true;
444             }
445         }
446         // We count on the basename file to be the definitive end, so stop here.
447         if (!dp->d_name[len] && found) {
448             break;
449         }
450     }
451     if (retval == log_time::EPOCH) {
452         return retval;
453     }
454     // tail_time prints matching or higher, round up by the modulo to prevent
455     // a replay of the last entry we have just checked.
456     retval += modulo;
457     return retval;
458 }
459 
460 } /* namespace android */
461 
462 
main(int argc,char ** argv)463 int main(int argc, char **argv)
464 {
465     using namespace android;
466     int err;
467     int hasSetLogFormat = 0;
468     int clearLog = 0;
469     int getLogSize = 0;
470     unsigned long setLogSize = 0;
471     int getPruneList = 0;
472     char *setPruneList = NULL;
473     int printStatistics = 0;
474     int mode = ANDROID_LOG_RDONLY;
475     const char *forceFilters = NULL;
476     log_device_t* devices = NULL;
477     log_device_t* dev;
478     bool printDividers = false;
479     struct logger_list *logger_list;
480     size_t tail_lines = 0;
481     log_time tail_time(log_time::EPOCH);
482 
483     signal(SIGPIPE, exit);
484 
485     g_logformat = android_log_format_new();
486 
487     if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
488         show_help(argv[0]);
489         return EXIT_SUCCESS;
490     }
491 
492     for (;;) {
493         int ret;
494 
495         ret = getopt(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:");
496 
497         if (ret < 0) {
498             break;
499         }
500 
501         switch(ret) {
502             case 's':
503                 // default to all silent
504                 android_log_addFilterRule(g_logformat, "*:s");
505             break;
506 
507             case 'c':
508                 clearLog = 1;
509                 mode |= ANDROID_LOG_WRONLY;
510             break;
511 
512             case 'L':
513                 mode |= ANDROID_LOG_PSTORE;
514             break;
515 
516             case 'd':
517                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
518             break;
519 
520             case 't':
521                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
522                 /* FALLTHRU */
523             case 'T':
524                 if (strspn(optarg, "0123456789") != strlen(optarg)) {
525                     char *cp = tail_time.strptime(optarg, g_defaultTimeFormat);
526                     if (!cp) {
527                         logcat_panic(false,
528                                     "-%c \"%s\" not in \"%s\" time format\n",
529                                     ret, optarg, g_defaultTimeFormat);
530                     }
531                     if (*cp) {
532                         char c = *cp;
533                         *cp = '\0';
534                         fprintf(stderr,
535                                 "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
536                                 ret, optarg, c, cp + 1);
537                         *cp = c;
538                     }
539                 } else {
540                     if (!getSizeTArg(optarg, &tail_lines, 1)) {
541                         fprintf(stderr,
542                                 "WARNING: -%c %s invalid, setting to 1\n",
543                                 ret, optarg);
544                         tail_lines = 1;
545                     }
546                 }
547             break;
548 
549             case 'D':
550                 printDividers = true;
551             break;
552 
553             case 'g':
554                 getLogSize = 1;
555             break;
556 
557             case 'G': {
558                 char *cp;
559                 if (strtoll(optarg, &cp, 0) > 0) {
560                     setLogSize = strtoll(optarg, &cp, 0);
561                 } else {
562                     setLogSize = 0;
563                 }
564 
565                 switch(*cp) {
566                 case 'g':
567                 case 'G':
568                     setLogSize *= 1024;
569                 /* FALLTHRU */
570                 case 'm':
571                 case 'M':
572                     setLogSize *= 1024;
573                 /* FALLTHRU */
574                 case 'k':
575                 case 'K':
576                     setLogSize *= 1024;
577                 /* FALLTHRU */
578                 case '\0':
579                 break;
580 
581                 default:
582                     setLogSize = 0;
583                 }
584 
585                 if (!setLogSize) {
586                     fprintf(stderr, "ERROR: -G <num><multiplier>\n");
587                     return EXIT_FAILURE;
588                 }
589             }
590             break;
591 
592             case 'p':
593                 getPruneList = 1;
594             break;
595 
596             case 'P':
597                 setPruneList = optarg;
598             break;
599 
600             case 'b': {
601                 if (strcmp(optarg, "all") == 0) {
602                     while (devices) {
603                         dev = devices;
604                         devices = dev->next;
605                         delete dev;
606                     }
607 
608                     devices = dev = NULL;
609                     g_devCount = 0;
610                     for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
611                         const char *name = android_log_id_to_name((log_id_t)i);
612                         log_id_t log_id = android_name_to_log_id(name);
613 
614                         if (log_id != (log_id_t)i) {
615                             continue;
616                         }
617 
618                         bool binary = strcmp(name, "events") == 0;
619                         log_device_t* d = new log_device_t(name, binary);
620 
621                         if (dev) {
622                             dev->next = d;
623                             dev = d;
624                         } else {
625                             devices = dev = d;
626                         }
627                         g_devCount++;
628                     }
629                     break;
630                 }
631 
632                 bool binary = strcmp(optarg, "events") == 0;
633 
634                 if (devices) {
635                     dev = devices;
636                     while (dev->next) {
637                         dev = dev->next;
638                     }
639                     dev->next = new log_device_t(optarg, binary);
640                 } else {
641                     devices = new log_device_t(optarg, binary);
642                 }
643                 g_devCount++;
644             }
645             break;
646 
647             case 'B':
648                 g_printBinary = 1;
649             break;
650 
651             case 'f':
652                 if ((tail_time == log_time::EPOCH) && (tail_lines != 0)) {
653                     tail_time = lastLogTime(optarg);
654                 }
655                 // redirect output to a file
656                 g_outputFileName = optarg;
657             break;
658 
659             case 'r':
660                 if (!getSizeTArg(optarg, &g_logRotateSizeKBytes, 1)) {
661                     logcat_panic(true, "Invalid parameter %s to -r\n", optarg);
662                 }
663             break;
664 
665             case 'n':
666                 if (!getSizeTArg(optarg, &g_maxRotatedLogs, 1)) {
667                     logcat_panic(true, "Invalid parameter %s to -n\n", optarg);
668                 }
669             break;
670 
671             case 'v':
672                 err = setLogFormat (optarg);
673                 if (err < 0) {
674                     logcat_panic(true, "Invalid parameter %s to -v\n", optarg);
675                 }
676                 hasSetLogFormat |= err;
677             break;
678 
679             case 'Q':
680                 /* this is a *hidden* option used to start a version of logcat                 */
681                 /* in an emulated device only. it basically looks for androidboot.logcat=      */
682                 /* on the kernel command line. If something is found, it extracts a log filter */
683                 /* and uses it to run the program. If nothing is found, the program should     */
684                 /* quit immediately                                                            */
685 #define  KERNEL_OPTION  "androidboot.logcat="
686 #define  CONSOLE_OPTION "androidboot.console="
687                 {
688                     int          fd;
689                     char*        logcat;
690                     char*        console;
691                     int          force_exit = 1;
692                     static char  cmdline[1024];
693 
694                     fd = open("/proc/cmdline", O_RDONLY);
695                     if (fd >= 0) {
696                         int  n = read(fd, cmdline, sizeof(cmdline)-1 );
697                         if (n < 0) n = 0;
698                         cmdline[n] = 0;
699                         close(fd);
700                     } else {
701                         cmdline[0] = 0;
702                     }
703 
704                     logcat  = strstr( cmdline, KERNEL_OPTION );
705                     console = strstr( cmdline, CONSOLE_OPTION );
706                     if (logcat != NULL) {
707                         char*  p = logcat + sizeof(KERNEL_OPTION)-1;;
708                         char*  q = strpbrk( p, " \t\n\r" );;
709 
710                         if (q != NULL)
711                             *q = 0;
712 
713                         forceFilters = p;
714                         force_exit   = 0;
715                     }
716                     /* if nothing found or invalid filters, exit quietly */
717                     if (force_exit) {
718                         return EXIT_SUCCESS;
719                     }
720 
721                     /* redirect our output to the emulator console */
722                     if (console) {
723                         char*  p = console + sizeof(CONSOLE_OPTION)-1;
724                         char*  q = strpbrk( p, " \t\n\r" );
725                         char   devname[64];
726                         int    len;
727 
728                         if (q != NULL) {
729                             len = q - p;
730                         } else
731                             len = strlen(p);
732 
733                         len = snprintf( devname, sizeof(devname), "/dev/%.*s", len, p );
734                         fprintf(stderr, "logcat using %s (%d)\n", devname, len);
735                         if (len < (int)sizeof(devname)) {
736                             fd = open( devname, O_WRONLY );
737                             if (fd >= 0) {
738                                 dup2(fd, 1);
739                                 dup2(fd, 2);
740                                 close(fd);
741                             }
742                         }
743                     }
744                 }
745                 break;
746 
747             case 'S':
748                 printStatistics = 1;
749                 break;
750 
751             case ':':
752                 logcat_panic(true, "Option -%c needs an argument\n", optopt);
753                 break;
754 
755             default:
756                 logcat_panic(true, "Unrecognized Option %c\n", optopt);
757                 break;
758         }
759     }
760 
761     if (!devices) {
762         dev = devices = new log_device_t("main", false);
763         g_devCount = 1;
764         if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
765             dev = dev->next = new log_device_t("system", false);
766             g_devCount++;
767         }
768         if (android_name_to_log_id("crash") == LOG_ID_CRASH) {
769             dev = dev->next = new log_device_t("crash", false);
770             g_devCount++;
771         }
772     }
773 
774     if (g_logRotateSizeKBytes != 0 && g_outputFileName == NULL) {
775         logcat_panic(true, "-r requires -f as well\n");
776     }
777 
778     setupOutput();
779 
780     if (hasSetLogFormat == 0) {
781         const char* logFormat = getenv("ANDROID_PRINTF_LOG");
782 
783         if (logFormat != NULL) {
784             err = setLogFormat(logFormat);
785             if (err < 0) {
786                 fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n",
787                                     logFormat);
788             }
789         } else {
790             setLogFormat("threadtime");
791         }
792     }
793 
794     if (forceFilters) {
795         err = android_log_addFilterString(g_logformat, forceFilters);
796         if (err < 0) {
797             logcat_panic(false, "Invalid filter expression in logcat args\n");
798         }
799     } else if (argc == optind) {
800         // Add from environment variable
801         char *env_tags_orig = getenv("ANDROID_LOG_TAGS");
802 
803         if (env_tags_orig != NULL) {
804             err = android_log_addFilterString(g_logformat, env_tags_orig);
805 
806             if (err < 0) {
807                 logcat_panic(true,
808                             "Invalid filter expression in ANDROID_LOG_TAGS\n");
809             }
810         }
811     } else {
812         // Add from commandline
813         for (int i = optind ; i < argc ; i++) {
814             err = android_log_addFilterString(g_logformat, argv[i]);
815 
816             if (err < 0) {
817                 logcat_panic(true, "Invalid filter expression '%s'\n", argv[i]);
818             }
819         }
820     }
821 
822     dev = devices;
823     if (tail_time != log_time::EPOCH) {
824         logger_list = android_logger_list_alloc_time(mode, tail_time, 0);
825     } else {
826         logger_list = android_logger_list_alloc(mode, tail_lines, 0);
827     }
828     while (dev) {
829         dev->logger_list = logger_list;
830         dev->logger = android_logger_open(logger_list,
831                                           android_name_to_log_id(dev->device));
832         if (!dev->logger) {
833             logcat_panic(false, "Unable to open log device '%s'\n",
834                          dev->device);
835         }
836 
837         if (clearLog) {
838             int ret;
839             ret = android_logger_clear(dev->logger);
840             if (ret) {
841                 logcat_panic(false, "failed to clear the log");
842             }
843         }
844 
845         if (setLogSize && android_logger_set_log_size(dev->logger, setLogSize)) {
846             logcat_panic(false, "failed to set the log size");
847         }
848 
849         if (getLogSize) {
850             long size, readable;
851 
852             size = android_logger_get_log_size(dev->logger);
853             if (size < 0) {
854                 logcat_panic(false, "failed to get the log size");
855             }
856 
857             readable = android_logger_get_log_readable_size(dev->logger);
858             if (readable < 0) {
859                 logcat_panic(false, "failed to get the readable log size");
860             }
861 
862             printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
863                    "max entry is %db, max payload is %db\n", dev->device,
864                    value_of_size(size), multiplier_of_size(size),
865                    value_of_size(readable), multiplier_of_size(readable),
866                    (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD);
867         }
868 
869         dev = dev->next;
870     }
871 
872     if (setPruneList) {
873         size_t len = strlen(setPruneList);
874         /*extra 32 bytes are needed by  android_logger_set_prune_list */
875         size_t bLen = len + 32;
876         char *buf = NULL;
877         if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
878             buf[len] = '\0';
879             if (android_logger_set_prune_list(logger_list, buf, bLen)) {
880                 logcat_panic(false, "failed to set the prune list");
881             }
882             free(buf);
883         } else {
884             logcat_panic(false, "failed to set the prune list (alloc)");
885         }
886     }
887 
888     if (printStatistics || getPruneList) {
889         size_t len = 8192;
890         char *buf;
891 
892         for(int retry = 32;
893                 (retry >= 0) && ((buf = new char [len]));
894                 delete [] buf, buf = NULL, --retry) {
895             if (getPruneList) {
896                 android_logger_get_prune_list(logger_list, buf, len);
897             } else {
898                 android_logger_get_statistics(logger_list, buf, len);
899             }
900             buf[len-1] = '\0';
901             if (atol(buf) < 3) {
902                 delete [] buf;
903                 buf = NULL;
904                 break;
905             }
906             size_t ret = atol(buf) + 1;
907             if (ret <= len) {
908                 len = ret;
909                 break;
910             }
911             len = ret;
912         }
913 
914         if (!buf) {
915             logcat_panic(false, "failed to read data");
916         }
917 
918         // remove trailing FF
919         char *cp = buf + len - 1;
920         *cp = '\0';
921         bool truncated = *--cp != '\f';
922         if (!truncated) {
923             *cp = '\0';
924         }
925 
926         // squash out the byte count
927         cp = buf;
928         if (!truncated) {
929             while (isdigit(*cp)) {
930                 ++cp;
931             }
932             if (*cp == '\n') {
933                 ++cp;
934             }
935         }
936 
937         printf("%s", cp);
938         delete [] buf;
939         return EXIT_SUCCESS;
940     }
941 
942 
943     if (getLogSize) {
944         return EXIT_SUCCESS;
945     }
946     if (setLogSize || setPruneList) {
947         return EXIT_SUCCESS;
948     }
949     if (clearLog) {
950         return EXIT_SUCCESS;
951     }
952 
953     //LOG_EVENT_INT(10, 12345);
954     //LOG_EVENT_LONG(11, 0x1122334455667788LL);
955     //LOG_EVENT_STRING(0, "whassup, doc?");
956 
957     dev = NULL;
958     log_device_t unexpected("unexpected", false);
959     while (1) {
960         struct log_msg log_msg;
961         log_device_t* d;
962         int ret = android_logger_list_read(logger_list, &log_msg);
963 
964         if (ret == 0) {
965             logcat_panic(false, "read: unexpected EOF!\n");
966         }
967 
968         if (ret < 0) {
969             if (ret == -EAGAIN) {
970                 break;
971             }
972 
973             if (ret == -EIO) {
974                 logcat_panic(false, "read: unexpected EOF!\n");
975             }
976             if (ret == -EINVAL) {
977                 logcat_panic(false, "read: unexpected length.\n");
978             }
979             logcat_panic(false, "logcat read failure");
980         }
981 
982         for(d = devices; d; d = d->next) {
983             if (android_name_to_log_id(d->device) == log_msg.id()) {
984                 break;
985             }
986         }
987         if (!d) {
988             g_devCount = 2; // set to Multiple
989             d = &unexpected;
990             d->binary = log_msg.id() == LOG_ID_EVENTS;
991         }
992 
993         if (dev != d) {
994             dev = d;
995             maybePrintStart(dev, printDividers);
996         }
997         if (g_printBinary) {
998             printBinary(&log_msg);
999         } else {
1000             processBuffer(dev, &log_msg);
1001         }
1002     }
1003 
1004     android_logger_list_free(logger_list);
1005 
1006     return EXIT_SUCCESS;
1007 }
1008