• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006-2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "logcat.h"
18 
19 #include <android-base/macros.h>
20 #include <arpa/inet.h>
21 #include <assert.h>
22 #include <ctype.h>
23 #include <dirent.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <getopt.h>
27 #include <math.h>
28 #include <pthread.h>
29 #include <sched.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/cdefs.h>
35 #include <sys/resource.h>
36 #include <sys/socket.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <time.h>
40 #include <unistd.h>
41 
42 #include <atomic>
43 #include <memory>
44 #include <string>
45 #include <utility>
46 #include <vector>
47 
48 #include <android-base/file.h>
49 #include <android-base/properties.h>
50 #include <android-base/stringprintf.h>
51 #include <android-base/strings.h>
52 #include <cutils/sockets.h>
53 #include <log/event_tag_map.h>
54 #include <log/logprint.h>
55 #include <private/android_logger.h>
56 #include <processgroup/sched_policy.h>
57 #include <system/thread_defs.h>
58 
59 #include <pcrecpp.h>
60 
61 #define DEFAULT_MAX_ROTATED_LOGS 4
62 
63 struct log_device_t {
64     const char* device;
65     bool binary;
66     struct logger* logger;
67     struct logger_list* logger_list;
68     bool printed;
69 
70     log_device_t* next;
71 
log_device_tlog_device_t72     log_device_t(const char* d, bool b) {
73         device = d;
74         binary = b;
75         next = nullptr;
76         printed = false;
77         logger = nullptr;
78         logger_list = nullptr;
79     }
80 };
81 
82 struct android_logcat_context_internal {
83     // status
84     volatile std::atomic_int retval;  // valid if thread_stopped set
85     // Arguments passed in, or copies and storage thereof if a thread.
86     int argc;
87     char* const* argv;
88     char* const* envp;
89     std::vector<std::string> args;
90     std::vector<const char*> argv_hold;
91     std::vector<std::string> envs;
92     std::vector<const char*> envp_hold;
93     int output_fd;  // duplication of fileno(output) (below)
94     int error_fd;   // duplication of fileno(error) (below)
95 
96     // library
97     int fds[2];    // From popen call
98     FILE* output;  // everything writes to fileno(output), buffer unused
99     FILE* error;   // unless error == output.
100     pthread_t thr;
101     volatile std::atomic_bool stop;  // quick exit flag
102     volatile std::atomic_bool thread_stopped;
103     bool stderr_null;    // shell "2>/dev/null"
104     bool stderr_stdout;  // shell "2>&1"
105 
106     // global variables
107     AndroidLogFormat* logformat;
108     const char* outputFileName;
109     // 0 means "no log rotation"
110     size_t logRotateSizeKBytes;
111     // 0 means "unbounded"
112     size_t maxRotatedLogs;
113     size_t outByteCount;
114     int printBinary;
115     int devCount;  // >1 means multiple
116     pcrecpp::RE* regex;
117     log_device_t* devices;
118     EventTagMap* eventTagMap;
119     // 0 means "infinite"
120     size_t maxCount;
121     size_t printCount;
122 
123     bool printItAnyways;
124     bool debug;
125     bool hasOpenedEventTagMap;
126 };
127 
128 // Creates a context associated with this logcat instance
create_android_logcat()129 android_logcat_context create_android_logcat() {
130     android_logcat_context_internal* context;
131 
132     context = (android_logcat_context_internal*)calloc(
133         1, sizeof(android_logcat_context_internal));
134     if (!context) return nullptr;
135 
136     context->fds[0] = -1;
137     context->fds[1] = -1;
138     context->output_fd = -1;
139     context->error_fd = -1;
140     context->maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS;
141 
142     context->argv_hold.clear();
143     context->args.clear();
144     context->envp_hold.clear();
145     context->envs.clear();
146 
147     return (android_logcat_context)context;
148 }
149 
150 // logd prefixes records with a length field
151 #define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t)
152 
153 namespace android {
154 
155 enum helpType { HELP_FALSE, HELP_TRUE, HELP_FORMAT };
156 
157 // if showHelp is set, newline required in fmt statement to transition to usage
158 static void logcat_panic(android_logcat_context_internal* context,
159                          enum helpType showHelp, const char* fmt, ...)
160     __printflike(3, 4);
161 
openLogFile(const char * pathname)162 static int openLogFile(const char* pathname) {
163     return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
164 }
165 
close_output(android_logcat_context_internal * context)166 static void close_output(android_logcat_context_internal* context) {
167     // split output_from_error
168     if (context->error == context->output) {
169         context->output = nullptr;
170         context->output_fd = -1;
171     }
172     if (context->error && (context->output_fd == fileno(context->error))) {
173         context->output_fd = -1;
174     }
175     if (context->output_fd == context->error_fd) {
176         context->output_fd = -1;
177     }
178     // close output channel
179     if (context->output) {
180         if (context->output != stdout) {
181             if (context->output_fd == fileno(context->output)) {
182                 context->output_fd = -1;
183             }
184             if (context->fds[1] == fileno(context->output)) {
185                 context->fds[1] = -1;
186             }
187             fclose(context->output);
188         }
189         context->output = nullptr;
190     }
191     if (context->output_fd >= 0) {
192         if (context->output_fd != fileno(stdout)) {
193             if (context->fds[1] == context->output_fd) {
194                 context->fds[1] = -1;
195             }
196             close(context->output_fd);
197         }
198         context->output_fd = -1;
199     }
200 }
201 
close_error(android_logcat_context_internal * context)202 static void close_error(android_logcat_context_internal* context) {
203     // split error_from_output
204     if (context->output == context->error) {
205         context->error = nullptr;
206         context->error_fd = -1;
207     }
208     if (context->output && (context->error_fd == fileno(context->output))) {
209         context->error_fd = -1;
210     }
211     if (context->error_fd == context->output_fd) {
212         context->error_fd = -1;
213     }
214     // close error channel
215     if (context->error) {
216         if ((context->error != stderr) && (context->error != stdout)) {
217             if (context->error_fd == fileno(context->error)) {
218                 context->error_fd = -1;
219             }
220             if (context->fds[1] == fileno(context->error)) {
221                 context->fds[1] = -1;
222             }
223             fclose(context->error);
224         }
225         context->error = nullptr;
226     }
227     if (context->error_fd >= 0) {
228         if ((context->error_fd != fileno(stdout)) &&
229             (context->error_fd != fileno(stderr))) {
230             if (context->fds[1] == context->error_fd) context->fds[1] = -1;
231             close(context->error_fd);
232         }
233         context->error_fd = -1;
234     }
235 }
236 
rotateLogs(android_logcat_context_internal * context)237 static void rotateLogs(android_logcat_context_internal* context) {
238     int err;
239 
240     // Can't rotate logs if we're not outputting to a file
241     if (!context->outputFileName) return;
242 
243     close_output(context);
244 
245     // Compute the maximum number of digits needed to count up to
246     // maxRotatedLogs in decimal.  eg:
247     // maxRotatedLogs == 30
248     //   -> log10(30) == 1.477
249     //   -> maxRotationCountDigits == 2
250     int maxRotationCountDigits =
251         (context->maxRotatedLogs > 0)
252             ? (int)(floor(log10(context->maxRotatedLogs) + 1))
253             : 0;
254 
255     for (int i = context->maxRotatedLogs; i > 0; i--) {
256         std::string file1 = android::base::StringPrintf(
257             "%s.%.*d", context->outputFileName, maxRotationCountDigits, i);
258 
259         std::string file0;
260         if (!(i - 1)) {
261             file0 = android::base::StringPrintf("%s", context->outputFileName);
262         } else {
263             file0 =
264                 android::base::StringPrintf("%s.%.*d", context->outputFileName,
265                                             maxRotationCountDigits, i - 1);
266         }
267 
268         if (!file0.length() || !file1.length()) {
269             perror("while rotating log files");
270             break;
271         }
272 
273         err = rename(file0.c_str(), file1.c_str());
274 
275         if (err < 0 && errno != ENOENT) {
276             perror("while rotating log files");
277         }
278     }
279 
280     context->output_fd = openLogFile(context->outputFileName);
281 
282     if (context->output_fd < 0) {
283         logcat_panic(context, HELP_FALSE, "couldn't open output file");
284         return;
285     }
286     context->output = fdopen(context->output_fd, "web");
287     if (!context->output) {
288         logcat_panic(context, HELP_FALSE, "couldn't fdopen output file");
289         return;
290     }
291     if (context->stderr_stdout) {
292         close_error(context);
293         context->error = context->output;
294         context->error_fd = context->output_fd;
295     }
296 
297     context->outByteCount = 0;
298 }
299 
printBinary(android_logcat_context_internal * context,struct log_msg * buf)300 void printBinary(android_logcat_context_internal* context, struct log_msg* buf) {
301     size_t size = buf->len();
302 
303     TEMP_FAILURE_RETRY(write(context->output_fd, buf, size));
304 }
305 
regexOk(android_logcat_context_internal * context,const AndroidLogEntry & entry)306 static bool regexOk(android_logcat_context_internal* context,
307                     const AndroidLogEntry& entry) {
308     if (!context->regex) return true;
309 
310     std::string messageString(entry.message, entry.messageLen);
311 
312     return context->regex->PartialMatch(messageString);
313 }
314 
processBuffer(android_logcat_context_internal * context,log_device_t * dev,struct log_msg * buf)315 static void processBuffer(android_logcat_context_internal* context,
316                           log_device_t* dev, struct log_msg* buf) {
317     int bytesWritten = 0;
318     int err;
319     AndroidLogEntry entry;
320     char binaryMsgBuf[1024];
321 
322     if (dev->binary) {
323         if (!context->eventTagMap && !context->hasOpenedEventTagMap) {
324             context->eventTagMap = android_openEventTagMap(nullptr);
325             context->hasOpenedEventTagMap = true;
326         }
327         err = android_log_processBinaryLogBuffer(
328             &buf->entry_v1, &entry, context->eventTagMap, binaryMsgBuf,
329             sizeof(binaryMsgBuf));
330         // printf(">>> pri=%d len=%d msg='%s'\n",
331         //    entry.priority, entry.messageLen, entry.message);
332     } else {
333         err = android_log_processLogBuffer(&buf->entry_v1, &entry);
334     }
335     if ((err < 0) && !context->debug) return;
336 
337     if (android_log_shouldPrintLine(
338             context->logformat, std::string(entry.tag, entry.tagLen).c_str(),
339             entry.priority)) {
340         bool match = regexOk(context, entry);
341 
342         context->printCount += match;
343         if (match || context->printItAnyways) {
344             bytesWritten = android_log_printLogLine(context->logformat,
345                                                     context->output_fd, &entry);
346 
347             if (bytesWritten < 0) {
348                 logcat_panic(context, HELP_FALSE, "output error");
349                 return;
350             }
351         }
352     }
353 
354     context->outByteCount += bytesWritten;
355 
356     if (context->logRotateSizeKBytes > 0 &&
357         (context->outByteCount / 1024) >= context->logRotateSizeKBytes) {
358         rotateLogs(context);
359     }
360 }
361 
maybePrintStart(android_logcat_context_internal * context,log_device_t * dev,bool printDividers)362 static void maybePrintStart(android_logcat_context_internal* context,
363                             log_device_t* dev, bool printDividers) {
364     if (!dev->printed || printDividers) {
365         if (context->devCount > 1 && !context->printBinary) {
366             char buf[1024];
367             snprintf(buf, sizeof(buf), "--------- %s %s\n",
368                      dev->printed ? "switch to" : "beginning of", dev->device);
369             if (write(context->output_fd, buf, strlen(buf)) < 0) {
370                 logcat_panic(context, HELP_FALSE, "output error");
371                 return;
372             }
373         }
374         dev->printed = true;
375     }
376 }
377 
setupOutputAndSchedulingPolicy(android_logcat_context_internal * context,bool blocking)378 static void setupOutputAndSchedulingPolicy(
379     android_logcat_context_internal* context, bool blocking) {
380     if (!context->outputFileName) return;
381 
382     if (blocking) {
383         // Lower priority and set to batch scheduling if we are saving
384         // the logs into files and taking continuous content.
385         if ((set_sched_policy(0, SP_BACKGROUND) < 0) && context->error) {
386             fprintf(context->error,
387                     "failed to set background scheduling policy\n");
388         }
389 
390         struct sched_param param;
391         memset(&param, 0, sizeof(param));
392         if (sched_setscheduler((pid_t)0, SCHED_BATCH, &param) < 0) {
393             fprintf(stderr, "failed to set to batch scheduler\n");
394         }
395 
396         if ((setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) &&
397             context->error) {
398             fprintf(context->error, "failed set to priority\n");
399         }
400     }
401 
402     close_output(context);
403 
404     context->output_fd = openLogFile(context->outputFileName);
405 
406     if (context->output_fd < 0) {
407         logcat_panic(context, HELP_FALSE, "couldn't open output file");
408         return;
409     }
410 
411     struct stat statbuf;
412     if (fstat(context->output_fd, &statbuf) == -1) {
413         close_output(context);
414         logcat_panic(context, HELP_FALSE, "couldn't get output file stat\n");
415         return;
416     }
417 
418     if ((size_t)statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
419         close_output(context);
420         logcat_panic(context, HELP_FALSE, "invalid output file stat\n");
421         return;
422     }
423 
424     context->output = fdopen(context->output_fd, "web");
425 
426     context->outByteCount = statbuf.st_size;
427 }
428 
429 // clang-format off
show_help(android_logcat_context_internal * context)430 static void show_help(android_logcat_context_internal* context) {
431     if (!context->error) return;
432 
433     const char* cmd = strrchr(context->argv[0], '/');
434     cmd = cmd ? cmd + 1 : context->argv[0];
435 
436     fprintf(context->error, "Usage: %s [options] [filterspecs]\n", cmd);
437 
438     fprintf(context->error, "options include:\n"
439                     "  -s              Set default filter to silent. Equivalent to filterspec '*:S'\n"
440                     "  -f <file>, --file=<file>               Log to file. Default is stdout\n"
441                     "  -r <kbytes>, --rotate-kbytes=<kbytes>\n"
442                     "                  Rotate log every kbytes. Requires -f option\n"
443                     "  -n <count>, --rotate-count=<count>\n"
444                     "                  Sets max number of rotated logs to <count>, default 4\n"
445                     "  --id=<id>       If the signature id for logging to file changes, then clear\n"
446                     "                  the fileset and continue\n"
447                     "  -v <format>, --format=<format>\n"
448                     "                  Sets log print format verb and adverbs, where <format> is:\n"
449                     "                    brief help long process raw tag thread threadtime time\n"
450                     "                  and individually flagged modifying adverbs can be added:\n"
451                     "                    color descriptive epoch monotonic printable uid\n"
452                     "                    usec UTC year zone\n"
453                     "                  Multiple -v parameters or comma separated list of format and\n"
454                     "                  format modifiers are allowed.\n"
455                     // private and undocumented nsec, no signal, too much noise
456                     // useful for -T or -t <timestamp> accurate testing though.
457                     "  -D, --dividers  Print dividers between each log buffer\n"
458                     "  -c, --clear     Clear (flush) the entire log and exit\n"
459                     "                  if Log to File specified, clear fileset instead\n"
460                     "  -d              Dump the log and then exit (don't block)\n"
461                     "  -e <expr>, --regex=<expr>\n"
462                     "                  Only print lines where the log message matches <expr>\n"
463                     "                  where <expr> is a Perl-compatible regular expression\n"
464                     // Leave --head undocumented as alias for -m
465                     "  -m <count>, --max-count=<count>\n"
466                     "                  Quit after printing <count> lines. This is meant to be\n"
467                     "                  paired with --regex, but will work on its own.\n"
468                     "  --print         Paired with --regex and --max-count to let content bypass\n"
469                     "                  regex filter but still stop at number of matches.\n"
470                     // Leave --tail undocumented as alias for -t
471                     "  -t <count>      Print only the most recent <count> lines (implies -d)\n"
472                     "  -t '<time>'     Print most recent lines since specified time (implies -d)\n"
473                     "  -T <count>      Print only the most recent <count> lines (does not imply -d)\n"
474                     "  -T '<time>'     Print most recent lines since specified time (not imply -d)\n"
475                     "                  count is pure numerical, time is 'MM-DD hh:mm:ss.mmm...'\n"
476                     "                  'YYYY-MM-DD hh:mm:ss.mmm...' or 'sssss.mmm...' format\n"
477                     "  -g, --buffer-size                      Get the size of the ring buffer.\n"
478                     "  -G <size>, --buffer-size=<size>\n"
479                     "                  Set size of log ring buffer, may suffix with K or M.\n"
480                     "  -L, --last      Dump logs from prior to last reboot\n"
481                     "  -b <buffer>, --buffer=<buffer>         Request alternate ring buffer, 'main',\n"
482                     "                  'system', 'radio', 'events', 'crash', 'default' or 'all'.\n"
483                     "                  Additionally, 'kernel' for userdebug and eng builds, and\n"
484                     "                  'security' for Device Owner installations.\n"
485                     "                  Multiple -b parameters or comma separated list of buffers are\n"
486                     "                  allowed. Buffers interleaved. Default -b main,system,crash.\n"
487                     "  -B, --binary    Output the log in binary.\n"
488                     "  -S, --statistics                       Output statistics.\n"
489                     "  -p, --prune     Print prune white and ~black list. Service is specified as\n"
490                     "                  UID, UID/PID or /PID. Weighed for quicker pruning if prefix\n"
491                     "                  with ~, otherwise weighed for longevity if unadorned. All\n"
492                     "                  other pruning activity is oldest first. Special case ~!\n"
493                     "                  represents an automatic quicker pruning for the noisiest\n"
494                     "                  UID as determined by the current statistics.\n"
495                     "  -P '<list> ...', --prune='<list> ...'\n"
496                     "                  Set prune white and ~black list, using same format as\n"
497                     "                  listed above. Must be quoted.\n"
498                     "  --pid=<pid>     Only prints logs from the given pid.\n"
499                     // Check ANDROID_LOG_WRAP_DEFAULT_TIMEOUT value for match to 2 hours
500                     "  --wrap          Sleep for 2 hours or when buffer about to wrap whichever\n"
501                     "                  comes first. Improves efficiency of polling by providing\n"
502                     "                  an about-to-wrap wakeup.\n");
503 
504     fprintf(context->error, "\nfilterspecs are a series of \n"
505                    "  <tag>[:priority]\n\n"
506                    "where <tag> is a log component tag (or * for all) and priority is:\n"
507                    "  V    Verbose (default for <tag>)\n"
508                    "  D    Debug (default for '*')\n"
509                    "  I    Info\n"
510                    "  W    Warn\n"
511                    "  E    Error\n"
512                    "  F    Fatal\n"
513                    "  S    Silent (suppress all output)\n"
514                    "\n'*' by itself means '*:D' and <tag> by itself means <tag>:V.\n"
515                    "If no '*' filterspec or -s on command line, all filter defaults to '*:V'.\n"
516                    "eg: '*:S <tag>' prints only <tag>, '<tag>:S' suppresses all <tag> log messages.\n"
517                    "\nIf not specified on the command line, filterspec is set from ANDROID_LOG_TAGS.\n"
518                    "\nIf not specified with -v on command line, format is set from ANDROID_PRINTF_LOG\n"
519                    "or defaults to \"threadtime\"\n\n");
520 }
521 
show_format_help(android_logcat_context_internal * context)522 static void show_format_help(android_logcat_context_internal* context) {
523     if (!context->error) return;
524     fprintf(context->error,
525         "-v <format>, --format=<format> options:\n"
526         "  Sets log print format verb and adverbs, where <format> is:\n"
527         "    brief long process raw tag thread threadtime time\n"
528         "  and individually flagged modifying adverbs can be added:\n"
529         "    color descriptive epoch monotonic printable uid usec UTC year zone\n"
530         "\nSingle format verbs:\n"
531         "  brief      — Display priority/tag and PID of the process issuing the message.\n"
532         "  long       — Display all metadata fields, separate messages with blank lines.\n"
533         "  process    — Display PID only.\n"
534         "  raw        — Display the raw log message, with no other metadata fields.\n"
535         "  tag        — Display the priority/tag only.\n"
536         "  thread     — Display priority, PID and TID of process issuing the message.\n"
537         "  threadtime — Display the date, invocation time, priority, tag, and the PID\n"
538         "               and TID of the thread issuing the message. (the default format).\n"
539         "  time       — Display the date, invocation time, priority/tag, and PID of the\n"
540         "             process issuing the message.\n"
541         "\nAdverb modifiers can be used in combination:\n"
542         "  color       — Display in highlighted color to match priority. i.e. \x1B[38;5;231mVERBOSE\n"
543         "                \x1B[38;5;75mDEBUG \x1B[38;5;40mINFO \x1B[38;5;166mWARNING \x1B[38;5;196mERROR FATAL\x1B[0m\n"
544         "  descriptive — events logs only, descriptions from event-log-tags database.\n"
545         "  epoch       — Display time as seconds since Jan 1 1970.\n"
546         "  monotonic   — Display time as cpu seconds since last boot.\n"
547         "  printable   — Ensure that any binary logging content is escaped.\n"
548         "  uid         — If permitted, display the UID or Android ID of logged process.\n"
549         "  usec        — Display time down the microsecond precision.\n"
550         "  UTC         — Display time as UTC.\n"
551         "  year        — Add the year to the displayed time.\n"
552         "  zone        — Add the local timezone to the displayed time.\n"
553         "  \"<zone>\"    — Print using this public named timezone (experimental).\n\n"
554     );
555 }
556 // clang-format on
557 
setLogFormat(android_logcat_context_internal * context,const char * formatString)558 static int setLogFormat(android_logcat_context_internal* context,
559                         const char* formatString) {
560     AndroidLogPrintFormat format;
561 
562     format = android_log_formatFromString(formatString);
563 
564     // invalid string?
565     if (format == FORMAT_OFF) return -1;
566 
567     return android_log_setPrintFormat(context->logformat, format);
568 }
569 
format_of_size(unsigned long value)570 static std::pair<unsigned long, const char*> format_of_size(unsigned long value) {
571     static const char multipliers[][3] = {{""}, {"Ki"}, {"Mi"}, {"Gi"}};
572     size_t i;
573     for (i = 0;
574          (i < sizeof(multipliers) / sizeof(multipliers[0])) && (value >= 1024);
575          value /= 1024, ++i)
576         ;
577     return std::make_pair(value, multipliers[i]);
578 }
579 
580 // String to unsigned int, returns -1 if it fails
getSizeTArg(const char * ptr,size_t * val,size_t min=0,size_t max=SIZE_MAX)581 static bool getSizeTArg(const char* ptr, size_t* val, size_t min = 0,
582                         size_t max = SIZE_MAX) {
583     if (!ptr) return false;
584 
585     char* endp;
586     errno = 0;
587     size_t ret = (size_t)strtoll(ptr, &endp, 0);
588 
589     if (endp[0] || errno) return false;
590 
591     if ((ret > max) || (ret < min)) return false;
592 
593     *val = ret;
594     return true;
595 }
596 
logcat_panic(android_logcat_context_internal * context,enum helpType showHelp,const char * fmt,...)597 static void logcat_panic(android_logcat_context_internal* context,
598                          enum helpType showHelp, const char* fmt, ...) {
599     context->retval = EXIT_FAILURE;
600     if (!context->error) {
601         context->stop = true;
602         return;
603     }
604 
605     va_list args;
606     va_start(args, fmt);
607     vfprintf(context->error, fmt, args);
608     va_end(args);
609 
610     switch (showHelp) {
611         case HELP_TRUE:
612             show_help(context);
613             break;
614         case HELP_FORMAT:
615             show_format_help(context);
616             break;
617         case HELP_FALSE:
618         default:
619             break;
620     }
621 
622     context->stop = true;
623 }
624 
parseTime(log_time & t,const char * cp)625 static char* parseTime(log_time& t, const char* cp) {
626     char* ep = t.strptime(cp, "%m-%d %H:%M:%S.%q");
627     if (ep) return ep;
628     ep = t.strptime(cp, "%Y-%m-%d %H:%M:%S.%q");
629     if (ep) return ep;
630     return t.strptime(cp, "%s.%q");
631 }
632 
633 // Find last logged line in <outputFileName>, or <outputFileName>.1
lastLogTime(const char * outputFileName)634 static log_time lastLogTime(const char* outputFileName) {
635     log_time retval(log_time::EPOCH);
636     if (!outputFileName) return retval;
637 
638     std::string directory;
639     const char* file = strrchr(outputFileName, '/');
640     if (!file) {
641         directory = ".";
642         file = outputFileName;
643     } else {
644         directory = std::string(outputFileName, file - outputFileName);
645         ++file;
646     }
647 
648     std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(directory.c_str()),
649                                             closedir);
650     if (!dir.get()) return retval;
651 
652     log_time now(android_log_clockid());
653 
654     size_t len = strlen(file);
655     log_time modulo(0, NS_PER_SEC);
656     struct dirent* dp;
657 
658     while (!!(dp = readdir(dir.get()))) {
659         if ((dp->d_type != DT_REG) || !!strncmp(dp->d_name, file, len) ||
660             (dp->d_name[len] && ((dp->d_name[len] != '.') ||
661                                  (strtoll(dp->d_name + 1, nullptr, 10) != 1)))) {
662             continue;
663         }
664 
665         std::string file_name = directory;
666         file_name += "/";
667         file_name += dp->d_name;
668         std::string file;
669         if (!android::base::ReadFileToString(file_name, &file)) continue;
670 
671         bool found = false;
672         for (const auto& line : android::base::Split(file, "\n")) {
673             log_time t(log_time::EPOCH);
674             char* ep = parseTime(t, line.c_str());
675             if (!ep || (*ep != ' ')) continue;
676             // determine the time precision of the logs (eg: msec or usec)
677             for (unsigned long mod = 1UL; mod < modulo.tv_nsec; mod *= 10) {
678                 if (t.tv_nsec % (mod * 10)) {
679                     modulo.tv_nsec = mod;
680                     break;
681                 }
682             }
683             // We filter any times later than current as we may not have the
684             // year stored with each log entry. Also, since it is possible for
685             // entries to be recorded out of order (very rare) we select the
686             // maximum we find just in case.
687             if ((t < now) && (t > retval)) {
688                 retval = t;
689                 found = true;
690             }
691         }
692         // We count on the basename file to be the definitive end, so stop here.
693         if (!dp->d_name[len] && found) break;
694     }
695     if (retval == log_time::EPOCH) return retval;
696     // tail_time prints matching or higher, round up by the modulo to prevent
697     // a replay of the last entry we have just checked.
698     retval += modulo;
699     return retval;
700 }
701 
getenv(android_logcat_context_internal * context,const char * name)702 const char* getenv(android_logcat_context_internal* context, const char* name) {
703     if (!context->envp || !name || !*name) return nullptr;
704 
705     for (size_t len = strlen(name), i = 0; context->envp[i]; ++i) {
706         if (strncmp(context->envp[i], name, len)) continue;
707         if (context->envp[i][len] == '=') return &context->envp[i][len + 1];
708     }
709     return nullptr;
710 }
711 
712 }  // namespace android
713 
reportErrorName(const char ** current,const char * name,bool blockSecurity)714 void reportErrorName(const char** current, const char* name,
715                      bool blockSecurity) {
716     if (*current) return;
717     if (!blockSecurity || (android_name_to_log_id(name) != LOG_ID_SECURITY)) {
718         *current = name;
719     }
720 }
721 
__logcat(android_logcat_context_internal * context)722 static int __logcat(android_logcat_context_internal* context) {
723     using namespace android;
724     int err;
725     bool hasSetLogFormat = false;
726     bool clearLog = false;
727     bool allSelected = false;
728     bool getLogSize = false;
729     bool getPruneList = false;
730     bool printStatistics = false;
731     bool printDividers = false;
732     unsigned long setLogSize = 0;
733     const char* setPruneList = nullptr;
734     const char* setId = nullptr;
735     int mode = ANDROID_LOG_RDONLY;
736     std::string forceFilters;
737     log_device_t* dev;
738     struct logger_list* logger_list;
739     size_t tail_lines = 0;
740     log_time tail_time(log_time::EPOCH);
741     size_t pid = 0;
742     bool got_t = false;
743 
744     // object instantiations before goto's can happen
745     log_device_t unexpected("unexpected", false);
746     const char* openDeviceFail = nullptr;
747     const char* clearFail = nullptr;
748     const char* setSizeFail = nullptr;
749     const char* getSizeFail = nullptr;
750     int argc = context->argc;
751     char* const* argv = context->argv;
752 
753     context->output = stdout;
754     context->error = stderr;
755 
756     for (int i = 0; i < argc; ++i) {
757         // Simulate shell stderr redirect parsing
758         if ((argv[i][0] != '2') || (argv[i][1] != '>')) continue;
759 
760         // Append to file not implemented, just open file
761         size_t skip = (argv[i][2] == '>') + 2;
762         if (!strcmp(&argv[i][skip], "/dev/null")) {
763             context->stderr_null = true;
764         } else if (!strcmp(&argv[i][skip], "&1")) {
765             context->stderr_stdout = true;
766         } else {
767             // stderr file redirections are not supported
768             fprintf(context->stderr_stdout ? stdout : stderr,
769                     "stderr redirection to file %s unsupported, skipping\n",
770                     &argv[i][skip]);
771         }
772         // Only the first one
773         break;
774     }
775 
776     const char* filename = nullptr;
777     for (int i = 0; i < argc; ++i) {
778         // Simulate shell stdout redirect parsing
779         if (argv[i][0] != '>') continue;
780 
781         // Append to file not implemented, just open file
782         filename = &argv[i][(argv[i][1] == '>') + 1];
783         // Only the first one
784         break;
785     }
786 
787     // Deal with setting up file descriptors and FILE pointers
788     if (context->error_fd >= 0) {  // Is an error file descriptor supplied?
789         if (context->error_fd == context->output_fd) {
790             context->stderr_stdout = true;
791         } else if (context->stderr_null) {  // redirection told us to close it
792             close(context->error_fd);
793             context->error_fd = -1;
794         } else {  // All Ok, convert error to a FILE pointer
795             context->error = fdopen(context->error_fd, "web");
796             if (!context->error) {
797                 context->retval = -errno;
798                 fprintf(context->stderr_stdout ? stdout : stderr,
799                         "Failed to fdopen(error_fd=%d) %s\n", context->error_fd,
800                         strerror(errno));
801                 goto exit;
802             }
803         }
804     }
805     if (context->output_fd >= 0) {  // Is an output file descriptor supplied?
806         if (filename) {  // redirect to file, close supplied file descriptor.
807             close(context->output_fd);
808             context->output_fd = -1;
809         } else {  // All Ok, convert output to a FILE pointer
810             context->output = fdopen(context->output_fd, "web");
811             if (!context->output) {
812                 context->retval = -errno;
813                 fprintf(context->stderr_stdout ? stdout : context->error,
814                         "Failed to fdopen(output_fd=%d) %s\n",
815                         context->output_fd, strerror(errno));
816                 goto exit;
817             }
818         }
819     }
820     if (filename) {  // We supplied an output file redirected in command line
821         context->output = fopen(filename, "web");
822     }
823     // Deal with 2>&1
824     if (context->stderr_stdout) context->error = context->output;
825     // Deal with 2>/dev/null
826     if (context->stderr_null) {
827         context->error_fd = -1;
828         context->error = nullptr;
829     }
830     // Only happens if output=stdout or output=filename
831     if ((context->output_fd < 0) && context->output) {
832         context->output_fd = fileno(context->output);
833     }
834     // Only happens if error=stdout || error=stderr
835     if ((context->error_fd < 0) && context->error) {
836         context->error_fd = fileno(context->error);
837     }
838 
839     context->logformat = android_log_format_new();
840 
841     if (argc == 2 && !strcmp(argv[1], "--help")) {
842         show_help(context);
843         context->retval = EXIT_SUCCESS;
844         goto exit;
845     }
846 
847     // meant to catch comma-delimited values, but cast a wider
848     // net for stability dealing with possible mistaken inputs.
849     static const char delimiters[] = ",:; \t\n\r\f";
850 
851     optind = 0;
852     while (true) {
853         int option_index = 0;
854         // list of long-argument only strings for later comparison
855         static const char pid_str[] = "pid";
856         static const char debug_str[] = "debug";
857         static const char id_str[] = "id";
858         static const char wrap_str[] = "wrap";
859         static const char print_str[] = "print";
860         // clang-format off
861         static const struct option long_options[] = {
862           { "binary",        no_argument,       nullptr, 'B' },
863           { "buffer",        required_argument, nullptr, 'b' },
864           { "buffer-size",   optional_argument, nullptr, 'g' },
865           { "clear",         no_argument,       nullptr, 'c' },
866           { debug_str,       no_argument,       nullptr, 0 },
867           { "dividers",      no_argument,       nullptr, 'D' },
868           { "file",          required_argument, nullptr, 'f' },
869           { "format",        required_argument, nullptr, 'v' },
870           // hidden and undocumented reserved alias for --regex
871           { "grep",          required_argument, nullptr, 'e' },
872           // hidden and undocumented reserved alias for --max-count
873           { "head",          required_argument, nullptr, 'm' },
874           { "help",          no_argument,       nullptr, 'h' },
875           { id_str,          required_argument, nullptr, 0 },
876           { "last",          no_argument,       nullptr, 'L' },
877           { "max-count",     required_argument, nullptr, 'm' },
878           { pid_str,         required_argument, nullptr, 0 },
879           { print_str,       no_argument,       nullptr, 0 },
880           { "prune",         optional_argument, nullptr, 'p' },
881           { "regex",         required_argument, nullptr, 'e' },
882           { "rotate-count",  required_argument, nullptr, 'n' },
883           { "rotate-kbytes", required_argument, nullptr, 'r' },
884           { "statistics",    no_argument,       nullptr, 'S' },
885           // hidden and undocumented reserved alias for -t
886           { "tail",          required_argument, nullptr, 't' },
887           // support, but ignore and do not document, the optional argument
888           { wrap_str,        optional_argument, nullptr, 0 },
889           { nullptr,         0,                 nullptr, 0 }
890         };
891         // clang-format on
892 
893         int c = getopt_long(argc, argv, ":cdDhLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", long_options,
894                             &option_index);
895         if (c == -1) break;
896 
897         switch (c) {
898             case 0:
899                 // only long options
900                 if (long_options[option_index].name == pid_str) {
901                     // ToDo: determine runtime PID_MAX?
902                     if (!getSizeTArg(optarg, &pid, 1)) {
903                         logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
904                                      long_options[option_index].name, optarg);
905                         goto exit;
906                     }
907                     break;
908                 }
909                 if (long_options[option_index].name == wrap_str) {
910                     mode |= ANDROID_LOG_WRAP | ANDROID_LOG_RDONLY |
911                             ANDROID_LOG_NONBLOCK;
912                     // ToDo: implement API that supports setting a wrap timeout
913                     size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
914                     if (optarg && !getSizeTArg(optarg, &dummy, 1)) {
915                         logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
916                                      long_options[option_index].name, optarg);
917                         goto exit;
918                     }
919                     if ((dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) &&
920                         context->error) {
921                         fprintf(context->error,
922                                 "WARNING: %s %u seconds, ignoring %zu\n",
923                                 long_options[option_index].name,
924                                 ANDROID_LOG_WRAP_DEFAULT_TIMEOUT, dummy);
925                     }
926                     break;
927                 }
928                 if (long_options[option_index].name == print_str) {
929                     context->printItAnyways = true;
930                     break;
931                 }
932                 if (long_options[option_index].name == debug_str) {
933                     context->debug = true;
934                     break;
935                 }
936                 if (long_options[option_index].name == id_str) {
937                     setId = (optarg && optarg[0]) ? optarg : nullptr;
938                 }
939                 break;
940 
941             case 's':
942                 // default to all silent
943                 android_log_addFilterRule(context->logformat, "*:s");
944                 break;
945 
946             case 'c':
947                 clearLog = true;
948                 mode |= ANDROID_LOG_WRONLY;
949                 break;
950 
951             case 'L':
952                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_PSTORE |
953                         ANDROID_LOG_NONBLOCK;
954                 break;
955 
956             case 'd':
957                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
958                 break;
959 
960             case 't':
961                 got_t = true;
962                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
963                 FALLTHROUGH_INTENDED;
964             case 'T':
965                 if (strspn(optarg, "0123456789") != strlen(optarg)) {
966                     char* cp = parseTime(tail_time, optarg);
967                     if (!cp) {
968                         logcat_panic(context, HELP_FALSE, "-%c \"%s\" not in time format\n", c,
969                                      optarg);
970                         goto exit;
971                     }
972                     if (*cp) {
973                         char ch = *cp;
974                         *cp = '\0';
975                         if (context->error) {
976                             fprintf(context->error, "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
977                                     c, optarg, ch, cp + 1);
978                         }
979                         *cp = ch;
980                     }
981                 } else {
982                     if (!getSizeTArg(optarg, &tail_lines, 1)) {
983                         if (context->error) {
984                             fprintf(context->error, "WARNING: -%c %s invalid, setting to 1\n", c,
985                                     optarg);
986                         }
987                         tail_lines = 1;
988                     }
989                 }
990                 break;
991 
992             case 'D':
993                 printDividers = true;
994                 break;
995 
996             case 'e':
997                 context->regex = new pcrecpp::RE(optarg);
998                 break;
999 
1000             case 'm': {
1001                 if (!getSizeTArg(optarg, &context->maxCount)) {
1002                     logcat_panic(context, HELP_FALSE,
1003                                  "-%c \"%s\" isn't an integer greater than zero\n", c, optarg);
1004                     goto exit;
1005                 }
1006             } break;
1007 
1008             case 'g':
1009                 if (!optarg) {
1010                     getLogSize = true;
1011                     break;
1012                 }
1013                 FALLTHROUGH_INTENDED;
1014 
1015             case 'G': {
1016                 char* cp;
1017                 if (strtoll(optarg, &cp, 0) > 0) {
1018                     setLogSize = strtoll(optarg, &cp, 0);
1019                 } else {
1020                     setLogSize = 0;
1021                 }
1022 
1023                 switch (*cp) {
1024                     case 'g':
1025                     case 'G':
1026                         setLogSize *= 1024;
1027                         FALLTHROUGH_INTENDED;
1028                     case 'm':
1029                     case 'M':
1030                         setLogSize *= 1024;
1031                         FALLTHROUGH_INTENDED;
1032                     case 'k':
1033                     case 'K':
1034                         setLogSize *= 1024;
1035                         FALLTHROUGH_INTENDED;
1036                     case '\0':
1037                         break;
1038 
1039                     default:
1040                         setLogSize = 0;
1041                 }
1042 
1043                 if (!setLogSize) {
1044                     logcat_panic(context, HELP_FALSE,
1045                                  "ERROR: -G <num><multiplier>\n");
1046                     goto exit;
1047                 }
1048             } break;
1049 
1050             case 'p':
1051                 if (!optarg) {
1052                     getPruneList = true;
1053                     break;
1054                 }
1055                 FALLTHROUGH_INTENDED;
1056 
1057             case 'P':
1058                 setPruneList = optarg;
1059                 break;
1060 
1061             case 'b': {
1062                 std::unique_ptr<char, void (*)(void*)> buffers(strdup(optarg), free);
1063                 char* arg = buffers.get();
1064                 unsigned idMask = 0;
1065                 char* sv = nullptr;  // protect against -ENOMEM above
1066                 while (!!(arg = strtok_r(arg, delimiters, &sv))) {
1067                     if (!strcmp(arg, "default")) {
1068                         idMask |= (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) |
1069                                   (1 << LOG_ID_CRASH);
1070                     } else if (!strcmp(arg, "all")) {
1071                         allSelected = true;
1072                         idMask = (unsigned)-1;
1073                     } else {
1074                         log_id_t log_id = android_name_to_log_id(arg);
1075                         const char* name = android_log_id_to_name(log_id);
1076 
1077                         if (!!strcmp(name, arg)) {
1078                             logcat_panic(context, HELP_TRUE,
1079                                          "unknown buffer %s\n", arg);
1080                             goto exit;
1081                         }
1082                         if (log_id == LOG_ID_SECURITY) allSelected = false;
1083                         idMask |= (1 << log_id);
1084                     }
1085                     arg = nullptr;
1086                 }
1087 
1088                 for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
1089                     const char* name = android_log_id_to_name((log_id_t)i);
1090                     log_id_t log_id = android_name_to_log_id(name);
1091 
1092                     if (log_id != (log_id_t)i) continue;
1093                     if (!(idMask & (1 << i))) continue;
1094 
1095                     bool found = false;
1096                     for (dev = context->devices; dev; dev = dev->next) {
1097                         if (!strcmp(name, dev->device)) {
1098                             found = true;
1099                             break;
1100                         }
1101                         if (!dev->next) break;
1102                     }
1103                     if (found) continue;
1104 
1105                     bool binary = !strcmp(name, "events") ||
1106                                   !strcmp(name, "security") ||
1107                                   !strcmp(name, "stats");
1108                     log_device_t* d = new log_device_t(name, binary);
1109 
1110                     if (dev) {
1111                         dev->next = d;
1112                         dev = d;
1113                     } else {
1114                         context->devices = dev = d;
1115                     }
1116                     context->devCount++;
1117                 }
1118             } break;
1119 
1120             case 'B':
1121                 context->printBinary = 1;
1122                 break;
1123 
1124             case 'f':
1125                 if ((tail_time == log_time::EPOCH) && !tail_lines) {
1126                     tail_time = lastLogTime(optarg);
1127                 }
1128                 // redirect output to a file
1129                 context->outputFileName = optarg;
1130                 break;
1131 
1132             case 'r':
1133                 if (!getSizeTArg(optarg, &context->logRotateSizeKBytes, 1)) {
1134                     logcat_panic(context, HELP_TRUE, "Invalid parameter \"%s\" to -r\n", optarg);
1135                     goto exit;
1136                 }
1137                 break;
1138 
1139             case 'n':
1140                 if (!getSizeTArg(optarg, &context->maxRotatedLogs, 1)) {
1141                     logcat_panic(context, HELP_TRUE, "Invalid parameter \"%s\" to -n\n", optarg);
1142                     goto exit;
1143                 }
1144                 break;
1145 
1146             case 'v': {
1147                 if (!strcmp(optarg, "help") || !strcmp(optarg, "--help")) {
1148                     show_format_help(context);
1149                     context->retval = EXIT_SUCCESS;
1150                     goto exit;
1151                 }
1152                 std::unique_ptr<char, void (*)(void*)> formats(strdup(optarg), free);
1153                 char* arg = formats.get();
1154                 char* sv = nullptr;  // protect against -ENOMEM above
1155                 while (!!(arg = strtok_r(arg, delimiters, &sv))) {
1156                     err = setLogFormat(context, arg);
1157                     if (err < 0) {
1158                         logcat_panic(context, HELP_FORMAT,
1159                                      "Invalid parameter \"%s\" to -v\n", arg);
1160                         goto exit;
1161                     }
1162                     arg = nullptr;
1163                     if (err) hasSetLogFormat = true;
1164                 }
1165             } break;
1166 
1167             case 'Q':
1168 #define LOGCAT_FILTER "androidboot.logcat="
1169 #define CONSOLE_PIPE_OPTION "androidboot.consolepipe="
1170 #define CONSOLE_OPTION "androidboot.console="
1171 #define QEMU_PROPERTY "ro.kernel.qemu"
1172 #define QEMU_CMDLINE "qemu.cmdline"
1173                 // This is a *hidden* option used to start a version of logcat
1174                 // in an emulated device only.  It basically looks for
1175                 // androidboot.logcat= on the kernel command line.  If
1176                 // something is found, it extracts a log filter and uses it to
1177                 // run the program. The logcat output will go to consolepipe if
1178                 // androiboot.consolepipe (e.g. qemu_pipe) is given, otherwise,
1179                 // it goes to androidboot.console (e.g. tty)
1180                 {
1181                     // if not in emulator, exit quietly
1182                     if (false == android::base::GetBoolProperty(QEMU_PROPERTY, false)) {
1183                         context->retval = EXIT_SUCCESS;
1184                         goto exit;
1185                     }
1186 
1187                     std::string cmdline = android::base::GetProperty(QEMU_CMDLINE, "");
1188                     if (cmdline.empty()) {
1189                         android::base::ReadFileToString("/proc/cmdline", &cmdline);
1190                     }
1191 
1192                     const char* logcatFilter = strstr(cmdline.c_str(), LOGCAT_FILTER);
1193                     // if nothing found or invalid filters, exit quietly
1194                     if (!logcatFilter) {
1195                         context->retval = EXIT_SUCCESS;
1196                         goto exit;
1197                     }
1198 
1199                     const char* p = logcatFilter + strlen(LOGCAT_FILTER);
1200                     const char* q = strpbrk(p, " \t\n\r");
1201                     if (!q) q = p + strlen(p);
1202                     forceFilters = std::string(p, q);
1203 
1204                     // redirect our output to the emulator console pipe or console
1205                     const char* consolePipe =
1206                         strstr(cmdline.c_str(), CONSOLE_PIPE_OPTION);
1207                     const char* console =
1208                         strstr(cmdline.c_str(), CONSOLE_OPTION);
1209 
1210                     if (consolePipe) {
1211                         p = consolePipe + strlen(CONSOLE_PIPE_OPTION);
1212                     } else if (console) {
1213                         p = console + strlen(CONSOLE_OPTION);
1214                     } else {
1215                         context->retval = EXIT_FAILURE;
1216                         goto exit;
1217                     }
1218 
1219                     q = strpbrk(p, " \t\n\r");
1220                     int len = q ? q - p : strlen(p);
1221                     std::string devname = "/dev/" + std::string(p, len);
1222                     std::string pipePurpose("pipe:logcat");
1223                     if (consolePipe) {
1224                         // example: "qemu_pipe,pipe:logcat"
1225                         // upon opening of /dev/qemu_pipe, the "pipe:logcat"
1226                         // string with trailing '\0' should be written to the fd
1227                         size_t pos = devname.find(',');
1228                         if (pos != std::string::npos) {
1229                             pipePurpose = devname.substr(pos + 1);
1230                             devname = devname.substr(0, pos);
1231                         }
1232                     }
1233                     cmdline.erase();
1234 
1235                     if (context->error) {
1236                         fprintf(context->error, "logcat using %s\n",
1237                                 devname.c_str());
1238                     }
1239 
1240                     FILE* fp = fopen(devname.c_str(), "web");
1241                     devname.erase();
1242                     if (!fp) break;
1243 
1244                     if (consolePipe) {
1245                         // need the trailing '\0'
1246                         if(!android::base::WriteFully(fileno(fp), pipePurpose.c_str(),
1247                                     pipePurpose.size() + 1)) {
1248                             fclose(fp);
1249                             context->retval = EXIT_FAILURE;
1250                             goto exit;
1251                         }
1252                     }
1253 
1254                     // close output and error channels, replace with console
1255                     android::close_output(context);
1256                     android::close_error(context);
1257                     context->stderr_stdout = true;
1258                     context->output = fp;
1259                     context->output_fd = fileno(fp);
1260                     if (context->stderr_null) break;
1261                     context->stderr_stdout = true;
1262                     context->error = fp;
1263                     context->error_fd = fileno(fp);
1264                 }
1265                 break;
1266 
1267             case 'S':
1268                 printStatistics = true;
1269                 break;
1270 
1271             case ':':
1272                 logcat_panic(context, HELP_TRUE, "Option -%c needs an argument\n", optopt);
1273                 goto exit;
1274 
1275             case 'h':
1276                 show_help(context);
1277                 show_format_help(context);
1278                 goto exit;
1279 
1280             default:
1281                 logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n", optopt);
1282                 goto exit;
1283         }
1284     }
1285 
1286     if (context->maxCount && got_t) {
1287         logcat_panic(context, HELP_TRUE,
1288                      "Cannot use -m (--max-count) and -t together\n");
1289         goto exit;
1290     }
1291     if (context->printItAnyways && (!context->regex || !context->maxCount)) {
1292         // One day it would be nice if --print -v color and --regex <expr>
1293         // could play with each other and show regex highlighted content.
1294         // clang-format off
1295         if (context->error) {
1296             fprintf(context->error, "WARNING: "
1297                             "--print ignored, to be used in combination with\n"
1298                                 "         "
1299                             "--regex <expr> and --max-count <N>\n");
1300         }
1301         context->printItAnyways = false;
1302     }
1303 
1304     if (!context->devices) {
1305         dev = context->devices = new log_device_t("main", false);
1306         context->devCount = 1;
1307         if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
1308             dev = dev->next = new log_device_t("system", false);
1309             context->devCount++;
1310         }
1311         if (android_name_to_log_id("crash") == LOG_ID_CRASH) {
1312             dev = dev->next = new log_device_t("crash", false);
1313             context->devCount++;
1314         }
1315     }
1316 
1317     if (!!context->logRotateSizeKBytes && !context->outputFileName) {
1318         logcat_panic(context, HELP_TRUE, "-r requires -f as well\n");
1319         goto exit;
1320     }
1321 
1322     if (!!setId) {
1323         if (!context->outputFileName) {
1324             logcat_panic(context, HELP_TRUE,
1325                          "--id='%s' requires -f as well\n", setId);
1326             goto exit;
1327         }
1328 
1329         std::string file_name = android::base::StringPrintf(
1330                                         "%s.id", context->outputFileName);
1331         std::string file;
1332         bool file_ok = android::base::ReadFileToString(file_name, &file);
1333         android::base::WriteStringToFile(setId, file_name, S_IRUSR | S_IWUSR,
1334                                          getuid(), getgid());
1335         if (!file_ok || !file.compare(setId)) setId = nullptr;
1336     }
1337 
1338     if (!hasSetLogFormat) {
1339         const char* logFormat = android::getenv(context, "ANDROID_PRINTF_LOG");
1340 
1341         if (!!logFormat) {
1342             std::unique_ptr<char, void (*)(void*)> formats(strdup(logFormat),
1343                                                            free);
1344             char* sv = nullptr;  // protect against -ENOMEM above
1345             char* arg = formats.get();
1346             while (!!(arg = strtok_r(arg, delimiters, &sv))) {
1347                 err = setLogFormat(context, arg);
1348                 // environment should not cause crash of logcat
1349                 if ((err < 0) && context->error) {
1350                     fprintf(context->error,
1351                             "invalid format in ANDROID_PRINTF_LOG '%s'\n", arg);
1352                 }
1353                 arg = nullptr;
1354                 if (err > 0) hasSetLogFormat = true;
1355             }
1356         }
1357         if (!hasSetLogFormat) {
1358             setLogFormat(context, "threadtime");
1359         }
1360     }
1361 
1362     if (forceFilters.size()) {
1363         err = android_log_addFilterString(context->logformat,
1364                                           forceFilters.c_str());
1365         if (err < 0) {
1366             logcat_panic(context, HELP_FALSE,
1367                          "Invalid filter expression in logcat args\n");
1368             goto exit;
1369         }
1370     } else if (argc == optind) {
1371         // Add from environment variable
1372         const char* env_tags_orig = android::getenv(context, "ANDROID_LOG_TAGS");
1373 
1374         if (!!env_tags_orig) {
1375             err = android_log_addFilterString(context->logformat,
1376                                               env_tags_orig);
1377 
1378             if (err < 0) {
1379                 logcat_panic(context, HELP_TRUE,
1380                             "Invalid filter expression in ANDROID_LOG_TAGS\n");
1381                 goto exit;
1382             }
1383         }
1384     } else {
1385         // Add from commandline
1386         for (int i = optind ; i < argc ; i++) {
1387             // skip stderr redirections of _all_ kinds
1388             if ((argv[i][0] == '2') && (argv[i][1] == '>')) continue;
1389             // skip stdout redirections of _all_ kinds
1390             if (argv[i][0] == '>') continue;
1391 
1392             err = android_log_addFilterString(context->logformat, argv[i]);
1393             if (err < 0) {
1394                 logcat_panic(context, HELP_TRUE,
1395                              "Invalid filter expression '%s'\n", argv[i]);
1396                 goto exit;
1397             }
1398         }
1399     }
1400 
1401     dev = context->devices;
1402     if (tail_time != log_time::EPOCH) {
1403         logger_list = android_logger_list_alloc_time(mode, tail_time, pid);
1404     } else {
1405         logger_list = android_logger_list_alloc(mode, tail_lines, pid);
1406     }
1407     // We have three orthogonal actions below to clear, set log size and
1408     // get log size. All sharing the same iteration loop.
1409     while (dev) {
1410         dev->logger_list = logger_list;
1411         dev->logger = android_logger_open(logger_list,
1412                                           android_name_to_log_id(dev->device));
1413         if (!dev->logger) {
1414             reportErrorName(&openDeviceFail, dev->device, allSelected);
1415             dev = dev->next;
1416             continue;
1417         }
1418 
1419         if (clearLog || setId) {
1420             if (context->outputFileName) {
1421                 int maxRotationCountDigits =
1422                     (context->maxRotatedLogs > 0) ?
1423                         (int)(floor(log10(context->maxRotatedLogs) + 1)) :
1424                         0;
1425 
1426                 for (int i = context->maxRotatedLogs ; i >= 0 ; --i) {
1427                     std::string file;
1428 
1429                     if (!i) {
1430                         file = android::base::StringPrintf(
1431                             "%s", context->outputFileName);
1432                     } else {
1433                         file = android::base::StringPrintf("%s.%.*d",
1434                             context->outputFileName, maxRotationCountDigits, i);
1435                     }
1436 
1437                     if (!file.length()) {
1438                         perror("while clearing log files");
1439                         reportErrorName(&clearFail, dev->device, allSelected);
1440                         break;
1441                     }
1442 
1443                     err = unlink(file.c_str());
1444 
1445                     if (err < 0 && errno != ENOENT && !clearFail) {
1446                         perror("while clearing log files");
1447                         reportErrorName(&clearFail, dev->device, allSelected);
1448                     }
1449                 }
1450             } else if (android_logger_clear(dev->logger)) {
1451                 reportErrorName(&clearFail, dev->device, allSelected);
1452             }
1453         }
1454 
1455         if (setLogSize) {
1456             if (android_logger_set_log_size(dev->logger, setLogSize)) {
1457                 reportErrorName(&setSizeFail, dev->device, allSelected);
1458             }
1459         }
1460 
1461         if (getLogSize) {
1462             long size = android_logger_get_log_size(dev->logger);
1463             long readable = android_logger_get_log_readable_size(dev->logger);
1464 
1465             if ((size < 0) || (readable < 0)) {
1466                 reportErrorName(&getSizeFail, dev->device, allSelected);
1467             } else {
1468                 auto size_format = format_of_size(size);
1469                 auto readable_format = format_of_size(readable);
1470                 std::string str = android::base::StringPrintf(
1471                        "%s: ring buffer is %lu %sB (%lu %sB consumed),"
1472                          " max entry is %d B, max payload is %d B\n",
1473                        dev->device,
1474                        size_format.first, size_format.second,
1475                        readable_format.first, readable_format.second,
1476                        (int)LOGGER_ENTRY_MAX_LEN,
1477                        (int)LOGGER_ENTRY_MAX_PAYLOAD);
1478                 TEMP_FAILURE_RETRY(write(context->output_fd,
1479                                          str.data(), str.length()));
1480             }
1481         }
1482 
1483         dev = dev->next;
1484     }
1485 
1486     context->retval = EXIT_SUCCESS;
1487 
1488     // report any errors in the above loop and exit
1489     if (openDeviceFail) {
1490         logcat_panic(context, HELP_FALSE,
1491                      "Unable to open log device '%s'\n", openDeviceFail);
1492         goto close;
1493     }
1494     if (clearFail) {
1495         logcat_panic(context, HELP_FALSE,
1496                      "failed to clear the '%s' log\n", clearFail);
1497         goto close;
1498     }
1499     if (setSizeFail) {
1500         logcat_panic(context, HELP_FALSE,
1501                      "failed to set the '%s' log size\n", setSizeFail);
1502         goto close;
1503     }
1504     if (getSizeFail) {
1505         logcat_panic(context, HELP_FALSE,
1506                      "failed to get the readable '%s' log size", getSizeFail);
1507         goto close;
1508     }
1509 
1510     if (setPruneList) {
1511         size_t len = strlen(setPruneList);
1512         // extra 32 bytes are needed by android_logger_set_prune_list
1513         size_t bLen = len + 32;
1514         char* buf = nullptr;
1515         if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
1516             buf[len] = '\0';
1517             if (android_logger_set_prune_list(logger_list, buf, bLen)) {
1518                 logcat_panic(context, HELP_FALSE,
1519                              "failed to set the prune list");
1520             }
1521             free(buf);
1522         } else {
1523             logcat_panic(context, HELP_FALSE,
1524                          "failed to set the prune list (alloc)");
1525         }
1526         goto close;
1527     }
1528 
1529     if (printStatistics || getPruneList) {
1530         size_t len = 8192;
1531         char* buf;
1532 
1533         for (int retry = 32; (retry >= 0) && ((buf = new char[len]));
1534              delete[] buf, buf = nullptr, --retry) {
1535             if (getPruneList) {
1536                 android_logger_get_prune_list(logger_list, buf, len);
1537             } else {
1538                 android_logger_get_statistics(logger_list, buf, len);
1539             }
1540             buf[len - 1] = '\0';
1541             if (atol(buf) < 3) {
1542                 delete[] buf;
1543                 buf = nullptr;
1544                 break;
1545             }
1546             size_t ret = atol(buf) + 1;
1547             if (ret <= len) {
1548                 len = ret;
1549                 break;
1550             }
1551             len = ret;
1552         }
1553 
1554         if (!buf) {
1555             logcat_panic(context, HELP_FALSE, "failed to read data");
1556             goto close;
1557         }
1558 
1559         // remove trailing FF
1560         char* cp = buf + len - 1;
1561         *cp = '\0';
1562         bool truncated = *--cp != '\f';
1563         if (!truncated) *cp = '\0';
1564 
1565         // squash out the byte count
1566         cp = buf;
1567         if (!truncated) {
1568             while (isdigit(*cp)) ++cp;
1569             if (*cp == '\n') ++cp;
1570         }
1571 
1572         len = strlen(cp);
1573         TEMP_FAILURE_RETRY(write(context->output_fd, cp, len));
1574         delete[] buf;
1575         goto close;
1576     }
1577 
1578     if (getLogSize || setLogSize || clearLog) goto close;
1579 
1580     setupOutputAndSchedulingPolicy(context, !(mode & ANDROID_LOG_NONBLOCK));
1581     if (context->stop) goto close;
1582 
1583     // LOG_EVENT_INT(10, 12345);
1584     // LOG_EVENT_LONG(11, 0x1122334455667788LL);
1585     // LOG_EVENT_STRING(0, "whassup, doc?");
1586 
1587     dev = nullptr;
1588 
1589     while (!context->stop &&
1590            (!context->maxCount || (context->printCount < context->maxCount))) {
1591         struct log_msg log_msg;
1592         int ret = android_logger_list_read(logger_list, &log_msg);
1593         if (!ret) {
1594             logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
1595             break;
1596         }
1597 
1598         if (ret < 0) {
1599             if (ret == -EAGAIN) break;
1600 
1601             if (ret == -EIO) {
1602                 logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
1603                 break;
1604             }
1605             if (ret == -EINVAL) {
1606                 logcat_panic(context, HELP_FALSE, "read: unexpected length.\n");
1607                 break;
1608             }
1609             logcat_panic(context, HELP_FALSE, "logcat read failure\n");
1610             break;
1611         }
1612 
1613         log_device_t* d;
1614         for (d = context->devices; d; d = d->next) {
1615             if (android_name_to_log_id(d->device) == log_msg.id()) break;
1616         }
1617         if (!d) {
1618             context->devCount = 2; // set to Multiple
1619             d = &unexpected;
1620             d->binary = log_msg.id() == LOG_ID_EVENTS;
1621         }
1622 
1623         if (dev != d) {
1624             dev = d;
1625             maybePrintStart(context, dev, printDividers);
1626             if (context->stop) break;
1627         }
1628         if (context->printBinary) {
1629             printBinary(context, &log_msg);
1630         } else {
1631             processBuffer(context, dev, &log_msg);
1632         }
1633     }
1634 
1635 close:
1636     // Short and sweet. Implemented generic version in android_logcat_destroy.
1637     while (!!(dev = context->devices)) {
1638         context->devices = dev->next;
1639         delete dev;
1640     }
1641     android_logger_list_free(logger_list);
1642 
1643 exit:
1644     // close write end of pipe to help things along
1645     if (context->output_fd == context->fds[1]) {
1646         android::close_output(context);
1647     }
1648     if (context->error_fd == context->fds[1]) {
1649         android::close_error(context);
1650     }
1651     if (context->fds[1] >= 0) {
1652         // NB: should be closed by the above
1653         int save_errno = errno;
1654         close(context->fds[1]);
1655         errno = save_errno;
1656         context->fds[1] = -1;
1657     }
1658     context->thread_stopped = true;
1659     return context->retval;
1660 }
1661 
1662 // Can block
android_logcat_run_command(android_logcat_context ctx,int output,int error,int argc,char * const * argv,char * const * envp)1663 int android_logcat_run_command(android_logcat_context ctx,
1664                                int output, int error,
1665                                int argc, char* const* argv,
1666                                char* const* envp) {
1667     android_logcat_context_internal* context = ctx;
1668 
1669     context->output_fd = output;
1670     context->error_fd = error;
1671     context->argc = argc;
1672     context->argv = argv;
1673     context->envp = envp;
1674     context->stop = false;
1675     context->thread_stopped = false;
1676     return __logcat(context);
1677 }
1678 
1679 // Finished with context
android_logcat_destroy(android_logcat_context * ctx)1680 int android_logcat_destroy(android_logcat_context* ctx) {
1681     android_logcat_context_internal* context = *ctx;
1682 
1683     if (!context) return -EBADF;
1684 
1685     *ctx = nullptr;
1686 
1687     context->stop = true;
1688 
1689     while (context->thread_stopped == false) {
1690         // Makes me sad, replace thread_stopped with semaphore.  Short lived.
1691         sched_yield();
1692     }
1693 
1694     delete context->regex;
1695     context->argv_hold.clear();
1696     context->args.clear();
1697     context->envp_hold.clear();
1698     context->envs.clear();
1699     if (context->fds[0] >= 0) {
1700         close(context->fds[0]);
1701         context->fds[0] = -1;
1702     }
1703     android::close_output(context);
1704     android::close_error(context);
1705 
1706     if (context->fds[1] >= 0) {
1707         // NB: this should be closed by close_output, but just in case...
1708         close(context->fds[1]);
1709         context->fds[1] = -1;
1710     }
1711 
1712     android_closeEventTagMap(context->eventTagMap);
1713 
1714     // generic cleanup of devices list to handle all possible dirty cases
1715     log_device_t* dev;
1716     while (!!(dev = context->devices)) {
1717         struct logger_list* logger_list = dev->logger_list;
1718         if (logger_list) {
1719             for (log_device_t* d = dev; d; d = d->next) {
1720                 if (d->logger_list == logger_list) d->logger_list = nullptr;
1721             }
1722             android_logger_list_free(logger_list);
1723         }
1724         context->devices = dev->next;
1725         delete dev;
1726     }
1727 
1728     int retval = context->retval;
1729 
1730     free(context);
1731 
1732     return retval;
1733 }
1734