logcat: Incorporate liblog reading API

Change-Id: I1e1a55d13ac55350e8d54bfe92b392de07f26a5d
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index d44c679..b6ca171 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -1,5 +1,6 @@
-// Copyright 2006 The Android Open Source Project
+// Copyright 2006-2013 The Android Open Source Project
 
+#include <log/log.h>
 #include <log/logger.h>
 #include <log/logd.h>
 #include <log/logprint.h>
@@ -24,65 +25,27 @@
 #define DEFAULT_MAX_ROTATED_LOGS 4
 
 static AndroidLogFormat * g_logformat;
-static bool g_nonblock = false;
-static int g_tail_lines = 0;
 
 /* logd prefixes records with a length field */
 #define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t)
 
-#define LOG_FILE_DIR    "/dev/log/"
-
-struct queued_entry_t {
-    union {
-        unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4)));
-        struct logger_entry entry __attribute__((aligned(4)));
-    };
-    queued_entry_t* next;
-
-    queued_entry_t() {
-        next = NULL;
-    }
-};
-
-static int cmp(queued_entry_t* a, queued_entry_t* b) {
-    int n = a->entry.sec - b->entry.sec;
-    if (n != 0) {
-        return n;
-    }
-    return a->entry.nsec - b->entry.nsec;
-}
-
 struct log_device_t {
-    char* device;
+    const char* device;
     bool binary;
-    int fd;
+    struct logger *logger;
+    struct logger_list *logger_list;
     bool printed;
     char label;
 
-    queued_entry_t* queue;
     log_device_t* next;
 
-    log_device_t(char* d, bool b, char l) {
+    log_device_t(const char* d, bool b, char l) {
         device = d;
         binary = b;
         label = l;
-        queue = NULL;
         next = NULL;
         printed = false;
     }
-
-    void enqueue(queued_entry_t* entry) {
-        if (this->queue == NULL) {
-            this->queue = entry;
-        } else {
-            queued_entry_t** e = &this->queue;
-            while (*e && cmp(entry, *e) >= 0) {
-                e = &((*e)->next);
-            }
-            entry->next = *e;
-            *e = entry;
-        }
-    }
 };
 
 namespace android {
@@ -147,17 +110,14 @@
 
 }
 
-void printBinary(struct logger_entry *buf)
+void printBinary(struct log_msg *buf)
 {
-    size_t size = sizeof(logger_entry) + buf->len;
-    int ret;
-    
-    do {
-        ret = write(g_outFD, buf, size);
-    } while (ret < 0 && errno == EINTR);
+    size_t size = buf->len();
+
+    TEMP_FAILURE_RETRY(write(g_outFD, buf, size));
 }
 
-static void processBuffer(log_device_t* dev, struct logger_entry *buf)
+static void processBuffer(log_device_t* dev, struct log_msg *buf)
 {
     int bytesWritten = 0;
     int err;
@@ -165,12 +125,14 @@
     char binaryMsgBuf[1024];
 
     if (dev->binary) {
-        err = android_log_processBinaryLogBuffer(buf, &entry, g_eventTagMap,
-                binaryMsgBuf, sizeof(binaryMsgBuf));
+        err = android_log_processBinaryLogBuffer(&buf->entry_v1, &entry,
+                                                 g_eventTagMap,
+                                                 binaryMsgBuf,
+                                                 sizeof(binaryMsgBuf));
         //printf(">>> pri=%d len=%d msg='%s'\n",
         //    entry.priority, entry.messageLen, entry.message);
     } else {
-        err = android_log_processLogBuffer(buf, &entry);
+        err = android_log_processLogBuffer(&buf->entry_v1, &entry);
     }
     if (err < 0) {
         goto error;
@@ -197,7 +159,7 @@
 
     g_outByteCount += bytesWritten;
 
-    if (g_logRotateSizeKBytes > 0 
+    if (g_logRotateSizeKBytes > 0
         && (g_outByteCount / 1024) >= g_logRotateSizeKBytes
     ) {
         rotateLogs();
@@ -208,20 +170,13 @@
     return;
 }
 
-static void chooseFirst(log_device_t* dev, log_device_t** firstdev) {
-    for (*firstdev = NULL; dev != NULL; dev = dev->next) {
-        if (dev->queue != NULL && (*firstdev == NULL || cmp(dev->queue, (*firstdev)->queue) < 0)) {
-            *firstdev = dev;
-        }
-    }
-}
-
 static void maybePrintStart(log_device_t* dev) {
     if (!dev->printed) {
         dev->printed = true;
         if (g_devCount > 1 && !g_printBinary) {
             char buf[1024];
-            snprintf(buf, sizeof(buf), "--------- beginning of %s\n", dev->device);
+            snprintf(buf, sizeof(buf), "--------- beginning of %s\n",
+                     dev->device);
             if (write(g_outFD, buf, strlen(buf)) < 0) {
                 perror("output error");
                 exit(-1);
@@ -230,145 +185,6 @@
     }
 }
 
-static void skipNextEntry(log_device_t* dev) {
-    maybePrintStart(dev);
-    queued_entry_t* entry = dev->queue;
-    dev->queue = entry->next;
-    delete entry;
-}
-
-static void printNextEntry(log_device_t* dev) {
-    maybePrintStart(dev);
-    if (g_printBinary) {
-        printBinary(&dev->queue->entry);
-    } else {
-        processBuffer(dev, &dev->queue->entry);
-    }
-    skipNextEntry(dev);
-}
-
-static void readLogLines(log_device_t* devices)
-{
-    log_device_t* dev;
-    int max = 0;
-    int ret;
-    int queued_lines = 0;
-    bool sleep = false;
-
-    int result;
-    fd_set readset;
-
-    for (dev=devices; dev; dev = dev->next) {
-        if (dev->fd > max) {
-            max = dev->fd;
-        }
-    }
-
-    while (1) {
-        do {
-            timeval timeout = { 0, 5000 /* 5ms */ }; // If we oversleep it's ok, i.e. ignore EINTR.
-            FD_ZERO(&readset);
-            for (dev=devices; dev; dev = dev->next) {
-                FD_SET(dev->fd, &readset);
-            }
-            result = select(max + 1, &readset, NULL, NULL, sleep ? NULL : &timeout);
-        } while (result == -1 && errno == EINTR);
-
-        if (result >= 0) {
-            for (dev=devices; dev; dev = dev->next) {
-                if (FD_ISSET(dev->fd, &readset)) {
-                    queued_entry_t* entry = new queued_entry_t();
-                    /* NOTE: driver guarantees we read exactly one full entry */
-                    ret = read(dev->fd, entry->buf, LOGGER_ENTRY_MAX_LEN);
-                    if (ret < 0) {
-                        if (errno == EINTR) {
-                            delete entry;
-                            goto next;
-                        }
-                        if (errno == EAGAIN) {
-                            delete entry;
-                            break;
-                        }
-                        perror("logcat read");
-                        exit(EXIT_FAILURE);
-                    }
-                    else if (!ret) {
-                        fprintf(stderr, "read: Unexpected EOF!\n");
-                        exit(EXIT_FAILURE);
-                    }
-                    else if (entry->entry.len != ret - sizeof(struct logger_entry)) {
-                        fprintf(stderr, "read: unexpected length. Expected %d, got %d\n",
-                                entry->entry.len, ret - sizeof(struct logger_entry));
-                        exit(EXIT_FAILURE);
-                    }
-
-                    entry->entry.msg[entry->entry.len] = '\0';
-
-                    dev->enqueue(entry);
-                    ++queued_lines;
-                }
-            }
-
-            if (result == 0) {
-                // we did our short timeout trick and there's nothing new
-                // print everything we have and wait for more data
-                sleep = true;
-                while (true) {
-                    chooseFirst(devices, &dev);
-                    if (dev == NULL) {
-                        break;
-                    }
-                    if (g_tail_lines == 0 || queued_lines <= g_tail_lines) {
-                        printNextEntry(dev);
-                    } else {
-                        skipNextEntry(dev);
-                    }
-                    --queued_lines;
-                }
-
-                // the caller requested to just dump the log and exit
-                if (g_nonblock) {
-                    return;
-                }
-            } else {
-                // print all that aren't the last in their list
-                sleep = false;
-                while (g_tail_lines == 0 || queued_lines > g_tail_lines) {
-                    chooseFirst(devices, &dev);
-                    if (dev == NULL || dev->queue->next == NULL) {
-                        break;
-                    }
-                    if (g_tail_lines == 0) {
-                        printNextEntry(dev);
-                    } else {
-                        skipNextEntry(dev);
-                    }
-                    --queued_lines;
-                }
-            }
-        }
-next:
-        ;
-    }
-}
-
-static int clearLog(int logfd)
-{
-    return ioctl(logfd, LOGGER_FLUSH_LOG);
-}
-
-/* returns the total size of the log's ring buffer */
-static int getLogSize(int logfd)
-{
-    return ioctl(logfd, LOGGER_GET_LOG_BUF_SIZE);
-}
-
-/* returns the readable size of the log's ring buffer (that is, amount of the log consumed) */
-static int getLogReadableSize(int logfd)
-{
-    return ioctl(logfd, LOGGER_GET_LOG_LEN);
-}
-
 static void setupOutput()
 {
 
@@ -465,6 +281,8 @@
     log_device_t* devices = NULL;
     log_device_t* dev;
     bool needBinary = false;
+    struct logger_list *logger_list;
+    int tail_lines = 0;
 
     g_logformat = android_log_format_new();
 
@@ -488,7 +306,7 @@
         }
 
         switch(ret) {
-            case 's': 
+            case 's':
                 // default to all silent
                 android_log_addFilterRule(g_logformat, "*:s");
             break;
@@ -499,12 +317,12 @@
             break;
 
             case 'd':
-                g_nonblock = true;
+                mode = O_RDONLY | O_NDELAY;
             break;
 
             case 't':
-                g_nonblock = true;
-                g_tail_lines = atoi(optarg);
+                mode = O_RDONLY | O_NDELAY;
+                tail_lines = atoi(optarg);
             break;
 
             case 'g':
@@ -512,10 +330,6 @@
             break;
 
             case 'b': {
-                char* buf = (char*) malloc(strlen(LOG_FILE_DIR) + strlen(optarg) + 1);
-                strcpy(buf, LOG_FILE_DIR);
-                strcat(buf, optarg);
-
                 bool binary = strcmp(optarg, "events") == 0;
                 if (binary) {
                     needBinary = true;
@@ -526,9 +340,9 @@
                     while (dev->next) {
                         dev = dev->next;
                     }
-                    dev->next = new log_device_t(buf, binary, optarg[0]);
+                    dev->next = new log_device_t(optarg, binary, optarg[0]);
                 } else {
-                    devices = new log_device_t(buf, binary, optarg[0]);
+                    devices = new log_device_t(optarg, binary, optarg[0]);
                 }
                 android::g_devCount++;
             }
@@ -546,8 +360,8 @@
             break;
 
             case 'r':
-                if (optarg == NULL) {                
-                    android::g_logRotateSizeKBytes 
+                if (optarg == NULL) {
+                    android::g_logRotateSizeKBytes
                                 = DEFAULT_LOG_ROTATE_SIZE_KBYTES;
                 } else {
                     long logRotateSize;
@@ -659,19 +473,15 @@
     }
 
     if (!devices) {
-        devices = new log_device_t(strdup("/dev/"LOGGER_LOG_MAIN), false, 'm');
+        devices = new log_device_t("main", false, 'm');
         android::g_devCount = 1;
-        int accessmode =
-                  (mode & O_RDONLY) ? R_OK : 0
-                | (mode & O_WRONLY) ? W_OK : 0;
-        // only add this if it's available
-        if (0 == access("/dev/"LOGGER_LOG_SYSTEM, accessmode)) {
-            devices->next = new log_device_t(strdup("/dev/"LOGGER_LOG_SYSTEM), false, 's');
+        if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
+            devices->next = new log_device_t("system", false, 's');
             android::g_devCount++;
         }
     }
 
-    if (android::g_logRotateSizeKBytes != 0 
+    if (android::g_logRotateSizeKBytes != 0
         && android::g_outputFileName == NULL
     ) {
         fprintf(stderr,"-r requires -f as well\n");
@@ -688,7 +498,7 @@
             err = setLogFormat(logFormat);
 
             if (err < 0) {
-                fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n", 
+                fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n",
                                     logFormat);
             }
         }
@@ -707,8 +517,8 @@
         if (env_tags_orig != NULL) {
             err = android_log_addFilterString(g_logformat, env_tags_orig);
 
-            if (err < 0) { 
-                fprintf(stderr, "Invalid filter expression in" 
+            if (err < 0) {
+                fprintf(stderr, "Invalid filter expression in"
                                     " ANDROID_LOG_TAGS\n");
                 android::show_help(argv[0]);
                 exit(-1);
@@ -719,7 +529,7 @@
         for (int i = optind ; i < argc ; i++) {
             err = android_log_addFilterString(g_logformat, argv[i]);
 
-            if (err < 0) { 
+            if (err < 0) {
                 fprintf (stderr, "Invalid filter expression '%s'\n", argv[i]);
                 android::show_help(argv[0]);
                 exit(-1);
@@ -728,19 +538,21 @@
     }
 
     dev = devices;
+    logger_list = android_logger_list_alloc(mode, tail_lines, 0);
     while (dev) {
-        dev->fd = open(dev->device, mode);
-        if (dev->fd < 0) {
-            fprintf(stderr, "Unable to open log device '%s': %s\n",
-                dev->device, strerror(errno));
+        dev->logger_list = logger_list;
+        dev->logger = android_logger_open(logger_list,
+                                          android_name_to_log_id(dev->device));
+        if (!dev->logger) {
+            fprintf(stderr, "Unable to open log device '%s'\n", dev->device);
             exit(EXIT_FAILURE);
         }
 
         if (clearLog) {
             int ret;
-            ret = android::clearLog(dev->fd);
+            ret = android_logger_clear(dev->logger);
             if (ret) {
-                perror("ioctl");
+                perror("clearLog");
                 exit(EXIT_FAILURE);
             }
         }
@@ -748,15 +560,15 @@
         if (getLogSize) {
             int size, readable;
 
-            size = android::getLogSize(dev->fd);
+            size = android_logger_get_log_size(dev->logger);
             if (size < 0) {
-                perror("ioctl");
+                perror("getLogSize");
                 exit(EXIT_FAILURE);
             }
 
-            readable = android::getLogReadableSize(dev->fd);
+            readable = android_logger_get_log_readable_size(dev->logger);
             if (readable < 0) {
-                perror("ioctl");
+                perror("getLogReadableSize");
                 exit(EXIT_FAILURE);
             }
 
@@ -783,7 +595,51 @@
     if (needBinary)
         android::g_eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE);
 
-    android::readLogLines(devices);
+    while (1) {
+        struct log_msg log_msg;
+        int ret = android_logger_list_read(logger_list, &log_msg);
+
+        if (ret == 0) {
+            fprintf(stderr, "read: Unexpected EOF!\n");
+            exit(EXIT_FAILURE);
+        }
+
+        if (ret < 0) {
+            if (ret == -EAGAIN) {
+                break;
+            }
+
+            if (ret == -EIO) {
+                fprintf(stderr, "read: Unexpected EOF!\n");
+                exit(EXIT_FAILURE);
+            }
+            if (ret == -EINVAL) {
+                fprintf(stderr, "read: unexpected length.\n");
+                exit(EXIT_FAILURE);
+            }
+            perror("logcat read");
+            exit(EXIT_FAILURE);
+        }
+
+        for(dev = devices; dev; dev = dev->next) {
+            if (android_name_to_log_id(dev->device) == log_msg.id()) {
+                break;
+            }
+        }
+        if (!dev) {
+            fprintf(stderr, "read: Unexpected log ID!\n");
+            exit(EXIT_FAILURE);
+        }
+
+        android::maybePrintStart(dev);
+        if (android::g_printBinary) {
+            android::printBinary(&log_msg);
+        } else {
+            android::processBuffer(dev, &log_msg);
+        }
+    }
+
+    android_logger_list_free(logger_list);
 
     return 0;
 }