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