Merge "fastboot: Support USB 3.x SuperSpeed devices on Linux"
diff --git a/adb/Android.mk b/adb/Android.mk
index 3cadee3..7136afb 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -64,8 +64,6 @@
 	$(EXTRA_SRCS) \
 	$(USB_SRCS) \
 
-LOCAL_C_INCLUDES += external/openssl/include
-
 ifneq ($(USE_SYSDEPS_WIN32),)
   LOCAL_SRC_FILES += sysdeps_win32.c
 else
@@ -77,7 +75,7 @@
 LOCAL_MODULE := adb
 LOCAL_MODULE_TAGS := debug
 
-LOCAL_STATIC_LIBRARIES := libzipfile libunz libcrypto_static $(EXTRA_STATIC_LIBS)
+LOCAL_STATIC_LIBRARIES := libzipfile libz libcrypto_static $(EXTRA_STATIC_LIBS)
 ifeq ($(USE_SYSDEPS_WIN32),)
 	LOCAL_STATIC_LIBRARIES += libcutils
 endif
@@ -167,11 +165,9 @@
 	-D_XOPEN_SOURCE \
 	-D_GNU_SOURCE
 
-LOCAL_C_INCLUDES += external/openssl/include
-
 LOCAL_MODULE := adb
 
-LOCAL_STATIC_LIBRARIES := libzipfile libunz libcutils liblog
+LOCAL_STATIC_LIBRARIES := libzipfile libz libcutils liblog
 
 LOCAL_SHARED_LIBRARIES := libcrypto
 
diff --git a/adb/usb_linux.c b/adb/usb_linux.c
index f16bdd0..7d13a5d 100644
--- a/adb/usb_linux.c
+++ b/adb/usb_linux.c
@@ -237,8 +237,20 @@
                             // looks like ADB...
                         ep1 = (struct usb_endpoint_descriptor *)bufptr;
                         bufptr += USB_DT_ENDPOINT_SIZE;
+                            // For USB 3.0 SuperSpeed devices, skip potential
+                            // USB 3.0 SuperSpeed Endpoint Companion descriptor
+                        if (bufptr+2 <= devdesc + desclength &&
+                            bufptr[0] == USB_DT_SS_EP_COMP_SIZE &&
+                            bufptr[1] == USB_DT_SS_ENDPOINT_COMP) {
+                            bufptr += USB_DT_SS_EP_COMP_SIZE;
+                        }
                         ep2 = (struct usb_endpoint_descriptor *)bufptr;
                         bufptr += USB_DT_ENDPOINT_SIZE;
+                        if (bufptr+2 <= devdesc + desclength &&
+                            bufptr[0] == USB_DT_SS_EP_COMP_SIZE &&
+                            bufptr[1] == USB_DT_SS_ENDPOINT_COMP) {
+                            bufptr += USB_DT_SS_EP_COMP_SIZE;
+                        }
 
                         if (bufptr > devdesc + desclength ||
                             ep1->bLength != USB_DT_ENDPOINT_SIZE ||
diff --git a/adb/usb_linux_client.c b/adb/usb_linux_client.c
index 8426e0e..70d2ad0 100644
--- a/adb/usb_linux_client.c
+++ b/adb/usb_linux_client.c
@@ -33,6 +33,7 @@
 
 #define MAX_PACKET_SIZE_FS	64
 #define MAX_PACKET_SIZE_HS	512
+#define MAX_PACKET_SIZE_SS	1024
 
 #define cpu_to_le16(x)  htole16(x)
 #define cpu_to_le32(x)  htole32(x)
@@ -56,19 +57,33 @@
 };
 
 static const struct {
-    struct usb_functionfs_descs_head header;
+    __le32 magic;
+    __le32 length;
+    __le32 flags;
+    __le32 fs_count;
+    __le32 hs_count;
+    __le32 ss_count;
     struct {
         struct usb_interface_descriptor intf;
         struct usb_endpoint_descriptor_no_audio source;
         struct usb_endpoint_descriptor_no_audio sink;
     } __attribute__((packed)) fs_descs, hs_descs;
+    struct {
+        struct usb_interface_descriptor intf;
+        struct usb_endpoint_descriptor_no_audio source;
+        struct usb_ss_ep_comp_descriptor source_comp;
+        struct usb_endpoint_descriptor_no_audio sink;
+        struct usb_ss_ep_comp_descriptor sink_comp;
+    } __attribute__((packed)) ss_descs;
 } __attribute__((packed)) descriptors = {
-    .header = {
-        .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC),
-        .length = cpu_to_le32(sizeof(descriptors)),
-        .fs_count = 3,
-        .hs_count = 3,
-    },
+    .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
+    .length = cpu_to_le32(sizeof(descriptors)),
+    .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC |
+                         FUNCTIONFS_HAS_HS_DESC |
+                         FUNCTIONFS_HAS_SS_DESC),
+    .fs_count = 3,
+    .hs_count = 3,
+    .ss_count = 5,
     .fs_descs = {
         .intf = {
             .bLength = sizeof(descriptors.fs_descs.intf),
@@ -121,6 +136,40 @@
             .wMaxPacketSize = MAX_PACKET_SIZE_HS,
         },
     },
+    .ss_descs = {
+        .intf = {
+            .bLength = sizeof(descriptors.ss_descs.intf),
+            .bDescriptorType = USB_DT_INTERFACE,
+            .bInterfaceNumber = 0,
+            .bNumEndpoints = 2,
+            .bInterfaceClass = ADB_CLASS,
+            .bInterfaceSubClass = ADB_SUBCLASS,
+            .bInterfaceProtocol = ADB_PROTOCOL,
+            .iInterface = 1, /* first string from the provided table */
+        },
+        .source = {
+            .bLength = sizeof(descriptors.ss_descs.source),
+            .bDescriptorType = USB_DT_ENDPOINT,
+            .bEndpointAddress = 1 | USB_DIR_OUT,
+            .bmAttributes = USB_ENDPOINT_XFER_BULK,
+            .wMaxPacketSize = MAX_PACKET_SIZE_SS,
+        },
+        .source_comp = {
+            .bLength = sizeof(descriptors.ss_descs.source_comp),
+            .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+        },
+        .sink = {
+            .bLength = sizeof(descriptors.ss_descs.sink),
+            .bDescriptorType = USB_DT_ENDPOINT,
+            .bEndpointAddress = 2 | USB_DIR_IN,
+            .bmAttributes = USB_ENDPOINT_XFER_BULK,
+            .wMaxPacketSize = MAX_PACKET_SIZE_SS,
+        },
+        .sink_comp = {
+            .bLength = sizeof(descriptors.ss_descs.sink_comp),
+            .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+        },
+    },
 };
 
 #define STR_INTERFACE_ "ADB Interface"
diff --git a/libutils/Android.mk b/libutils/Android.mk
index e738c15..52de910 100644
--- a/libutils/Android.mk
+++ b/libutils/Android.mk
@@ -53,13 +53,6 @@
 endif
 endif
 
-host_commonLdlibs :=
-
-ifeq ($(TARGET_OS),linux)
-host_commonLdlibs += -lrt -ldl
-endif
-
-
 # For the host
 # =====================================================
 include $(CLEAR_VARS)
@@ -93,9 +86,6 @@
 endif
 LOCAL_CFLAGS += -Werror
 
-LOCAL_C_INCLUDES += \
-	external/zlib
-
 LOCAL_STATIC_LIBRARIES := \
 	libcutils
 
diff --git a/libziparchive/Android.mk b/libziparchive/Android.mk
index 9bc6e61..ba7b74d 100644
--- a/libziparchive/Android.mk
+++ b/libziparchive/Android.mk
@@ -16,7 +16,6 @@
 LOCAL_PATH := $(call my-dir)
 
 source_files := zip_archive.cc
-includes := external/zlib
 
 include $(CLEAR_VARS)
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
@@ -25,16 +24,13 @@
 LOCAL_STATIC_LIBRARIES := libz
 LOCAL_SHARED_LIBRARIES := libutils
 LOCAL_MODULE:= libziparchive
-LOCAL_C_INCLUDES += ${includes}
 LOCAL_CFLAGS := -Werror
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_MODULE := libziparchive
 LOCAL_CPP_EXTENSION := .cc
 LOCAL_SRC_FILES := ${source_files}
-LOCAL_C_INCLUDES += ${includes}
 LOCAL_STATIC_LIBRARIES := libz libutils
 LOCAL_MODULE:= libziparchive-host
 LOCAL_CFLAGS := -Werror
@@ -46,10 +42,8 @@
 
 include $(CLEAR_VARS)
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_MODULE := libziparchive
 LOCAL_CPP_EXTENSION := .cc
 LOCAL_SRC_FILES := ${source_files}
-LOCAL_C_INCLUDES += ${includes}
 LOCAL_STATIC_LIBRARIES := libz libutils
 LOCAL_SHARED_LIBRARIES := liblog
 LOCAL_MODULE:= libziparchive-host
@@ -63,7 +57,7 @@
 LOCAL_MODULE := ziparchive-tests
 LOCAL_CPP_EXTENSION := .cc
 LOCAL_CFLAGS := -Werror
-LOCAL_SRC_FILES := zip_archive_test.cc
+LOCAL_SRC_FILES := zip_archive_test.cc entry_name_utils_test.cc
 LOCAL_SHARED_LIBRARIES := liblog
 LOCAL_STATIC_LIBRARIES := libziparchive libz libutils
 include $(BUILD_NATIVE_TEST)
@@ -75,7 +69,7 @@
 LOCAL_CFLAGS += \
     -Werror \
     -Wno-unnamed-type-template-args
-LOCAL_SRC_FILES := zip_archive_test.cc
+LOCAL_SRC_FILES := zip_archive_test.cc entry_name_utils_test.cc
 LOCAL_SHARED_LIBRARIES := libziparchive-host liblog
 LOCAL_STATIC_LIBRARIES := \
     libz \
diff --git a/libziparchive/entry_name_utils-inl.h b/libziparchive/entry_name_utils-inl.h
new file mode 100644
index 0000000..ddbc286
--- /dev/null
+++ b/libziparchive/entry_name_utils-inl.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
+#define LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+// Check if |length| bytes at |entry_name| constitute a valid entry name.
+// Entry names must be valid UTF-8 and must not contain '0'.
+inline bool IsValidEntryName(const uint8_t* entry_name, const size_t length) {
+  for (size_t i = 0; i < length; ++i) {
+    const uint8_t byte = entry_name[i];
+    if (byte == 0) {
+      return false;
+    } else if ((byte & 0x80) == 0) {
+      // Single byte sequence.
+      continue;
+    } else if ((byte & 0xc0) == 0x80 || (byte & 0xfe) == 0xfe) {
+      // Invalid sequence.
+      return false;
+    } else {
+      // 2-5 byte sequences.
+      for (uint8_t first = byte << 1; first & 0x80; first <<= 1) {
+        ++i;
+
+        // Missing continuation byte..
+        if (i == length) {
+          return false;
+        }
+
+        // Invalid continuation byte.
+        const uint8_t continuation_byte = entry_name[i];
+        if ((continuation_byte & 0xc0) != 0x80) {
+          return false;
+        }
+      }
+    }
+  }
+
+  return true;
+}
+
+
+#endif  // LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
diff --git a/libziparchive/entry_name_utils_test.cc b/libziparchive/entry_name_utils_test.cc
new file mode 100644
index 0000000..20715bb
--- /dev/null
+++ b/libziparchive/entry_name_utils_test.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "entry_name_utils-inl.h"
+
+#include <gtest/gtest.h>
+
+TEST(entry_name_utils, NullChars) {
+  // 'A', 'R', '\0', 'S', 'E'
+  const uint8_t zeroes[] = { 0x41, 0x52, 0x00, 0x53, 0x45 };
+  ASSERT_FALSE(IsValidEntryName(zeroes, sizeof(zeroes)));
+
+  const uint8_t zeroes_continuation_chars[] = { 0xc2, 0xa1, 0xc2, 0x00 };
+  ASSERT_FALSE(IsValidEntryName(zeroes_continuation_chars,
+                                sizeof(zeroes_continuation_chars)));
+}
+
+TEST(entry_name_utils, InvalidSequence) {
+  // 0xfe is an invalid start byte
+  const uint8_t invalid[] = { 0x41, 0xfe };
+  ASSERT_FALSE(IsValidEntryName(invalid, sizeof(invalid)));
+
+  // 0x91 is an invalid start byte (it's a valid continuation byte).
+  const uint8_t invalid2[] = { 0x41, 0x91 };
+  ASSERT_FALSE(IsValidEntryName(invalid2, sizeof(invalid2)));
+}
+
+TEST(entry_name_utils, TruncatedContinuation) {
+  // Malayalam script with truncated bytes. There should be 2 bytes
+  // after 0xe0
+  const uint8_t truncated[] = { 0xe0, 0xb4, 0x85, 0xe0, 0xb4 };
+  ASSERT_FALSE(IsValidEntryName(truncated, sizeof(truncated)));
+
+  // 0xc2 is the start of a 2 byte sequence that we've subsequently
+  // dropped.
+  const uint8_t truncated2[] = { 0xc2, 0xc2, 0xa1 };
+  ASSERT_FALSE(IsValidEntryName(truncated2, sizeof(truncated2)));
+}
+
+TEST(entry_name_utils, BadContinuation) {
+  // 0x41 is an invalid continuation char, since it's MSBs
+  // aren't "10..." (are 01).
+  const uint8_t bad[] = { 0xc2, 0xa1, 0xc2, 0x41 };
+  ASSERT_FALSE(IsValidEntryName(bad, sizeof(bad)));
+
+  // 0x41 is an invalid continuation char, since it's MSBs
+  // aren't "10..." (are 11).
+  const uint8_t bad2[] = { 0xc2, 0xa1, 0xc2, 0xfe };
+  ASSERT_FALSE(IsValidEntryName(bad2, sizeof(bad2)));
+}
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 92150c3..b6fd0d2 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -33,8 +33,10 @@
 
 #include <JNIHelp.h>  // TEMP_FAILURE_RETRY may or may not be in unistd
 
+#include "entry_name_utils-inl.h"
 #include "ziparchive/zip_archive.h"
 
+
 // This is for windows. If we don't open a file in binary mode, weird
 // things will happen.
 #ifndef O_BINARY
@@ -641,9 +643,8 @@
     const uint16_t comment_length = cdr->comment_length;
     const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
 
-    /* check that file name doesn't contain \0 character */
-    if (memchr(file_name, 0, file_name_length) != NULL) {
-      ALOGW("Zip: entry name can't contain \\0 character");
+    /* check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters */
+    if (!IsValidEntryName(file_name, file_name_length)) {
       goto bail;
     }
 
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 6306f5c..79f2ebd 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -455,36 +455,31 @@
                         delete dev;
                     }
 
-                    dev = devices = new log_device_t("main", false, 'm');
-                    android::g_devCount = 1;
-                    if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
-                        dev->next = new log_device_t("system", false, 's');
-                        if (dev->next) {
-                            dev = dev->next;
-                            android::g_devCount++;
+                    devices = dev = NULL;
+                    android::g_devCount = 0;
+                    needBinary = false;
+                    for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
+                        const char *name = android_log_id_to_name((log_id_t)i);
+                        log_id_t log_id = android_name_to_log_id(name);
+
+                        if (log_id != (log_id_t)i) {
+                            continue;
                         }
-                    }
-                    if (android_name_to_log_id("radio") == LOG_ID_RADIO) {
-                        dev->next = new log_device_t("radio", false, 'r');
-                        if (dev->next) {
-                            dev = dev->next;
-                            android::g_devCount++;
+
+                        bool binary = strcmp(name, "events") == 0;
+                        log_device_t* d = new log_device_t(name, binary, *name);
+
+                        if (dev) {
+                            dev->next = d;
+                            dev = d;
+                        } else {
+                            devices = dev = d;
                         }
-                    }
-                    if (android_name_to_log_id("events") == LOG_ID_EVENTS) {
-                        dev->next = new log_device_t("events", true, 'e');
-                        if (dev->next) {
-                            dev = dev->next;
-                            android::g_devCount++;
+                        android::g_devCount++;
+                        if (binary) {
                             needBinary = true;
                         }
                     }
-                    if (android_name_to_log_id("crash") == LOG_ID_CRASH) {
-                        dev->next = new log_device_t("crash", false, 'c');
-                        if (dev->next) {
-                            android::g_devCount++;
-                        }
-                    }
                     break;
                 }
 
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index ee2f32d..c7c0249 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -19,7 +19,6 @@
 #include <limits.h>
 #include <stdarg.h>
 #include <stdlib.h>
-#include <sys/klog.h>
 #include <sys/prctl.h>
 #include <sys/uio.h>
 #include <syslog.h>
@@ -33,21 +32,23 @@
     '0' + (LOG_AUTH | (PRI)) % 10, \
     '>'
 
-LogAudit::LogAudit(LogBuffer *buf, LogReader *reader, int fdDmsg)
+LogAudit::LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg)
         : SocketListener(getLogSocket(), false)
         , logbuf(buf)
         , reader(reader)
-        , fdDmesg(-1) {
+        , fdDmesg(fdDmesg)
+        , initialized(false) {
     static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
         'l', 'o', 'g', 'd', '.', 'a', 'u', 'd', 'i', 't', 'd', ':',
         ' ', 's', 't', 'a', 'r', 't', '\n' };
-    write(fdDmsg, auditd_message, sizeof(auditd_message));
-    logDmesg();
-    fdDmesg = fdDmsg;
+    write(fdDmesg, auditd_message, sizeof(auditd_message));
 }
 
 bool LogAudit::onDataAvailable(SocketClient *cli) {
-    prctl(PR_SET_NAME, "logd.auditd");
+    if (!initialized) {
+        prctl(PR_SET_NAME, "logd.auditd");
+        initialized = true;
+    }
 
     struct audit_message rep;
 
@@ -60,7 +61,8 @@
         return false;
     }
 
-    logPrint("type=%d %.*s", rep.nlh.nlmsg_type, rep.nlh.nlmsg_len, rep.data);
+    logPrint("type=%d %.*s",
+        rep.nlh.nlmsg_type, rep.nlh.nlmsg_len, rep.data);
 
     return true;
 }
@@ -87,7 +89,7 @@
     }
 
     bool info = strstr(str, " permissive=1") || strstr(str, " policy loaded ");
-    if (fdDmesg >= 0) {
+    if ((fdDmesg >= 0) && initialized) {
         struct iovec iov[3];
         static const char log_info[] = { KMSG_PRIORITY(LOG_INFO) };
         static const char log_warning[] = { KMSG_PRIORITY(LOG_WARNING) };
@@ -213,34 +215,23 @@
     return rc;
 }
 
-void LogAudit::logDmesg() {
-    int len = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
-    if (len <= 0) {
-        return;
+int LogAudit::log(char *buf) {
+    char *audit = strstr(buf, " audit(");
+    if (!audit) {
+        return 0;
     }
 
-    len++;
-    char buf[len];
+    *audit = '\0';
 
-    int rc = klogctl(KLOG_READ_ALL, buf, len);
-
-    buf[len - 1] = '\0';
-
-    for(char *tok = buf; (rc >= 0) && ((tok = strtok(tok, "\r\n"))); tok = NULL) {
-        char *audit = strstr(tok, " audit(");
-        if (!audit) {
-            continue;
-        }
-
-        *audit++ = '\0';
-
-        char *type = strstr(tok, "type=");
-        if (type) {
-            rc = logPrint("%s %s", type, audit);
-        } else {
-            rc = logPrint("%s", audit);
-        }
+    int rc;
+    char *type = strstr(buf, "type=");
+    if (type) {
+        rc = logPrint("%s %s", type, audit + 1);
+    } else {
+        rc = logPrint("%s", audit + 1);
     }
+    *audit = ' ';
+    return rc;
 }
 
 int LogAudit::getLogSocket() {
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index 111030a..f977be9 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -24,16 +24,17 @@
     LogBuffer *logbuf;
     LogReader *reader;
     int fdDmesg;
+    bool initialized;
 
 public:
     LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg);
+    int log(char *buf);
 
 protected:
     virtual bool onDataAvailable(SocketClient *cli);
 
 private:
     static int getLogSocket();
-    void logDmesg();
     int logPrint(const char *fmt, ...)
         __attribute__ ((__format__ (__printf__, 2, 3)));
 };
diff --git a/logd/main.cpp b/logd/main.cpp
index 54da7e3..946a9a0 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/capability.h>
+#include <sys/klog.h>
 #include <sys/prctl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -195,6 +196,23 @@
     if (auditd) {
         // failure is an option ... messages are in dmesg (required by standard)
         LogAudit *al = new LogAudit(logBuf, reader, fdDmesg);
+
+        int len = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
+        if (len > 0) {
+            len++;
+            char buf[len];
+
+            int rc = klogctl(KLOG_READ_ALL, buf, len);
+
+            buf[len - 1] = '\0';
+
+            for(char *ptr, *tok = buf;
+                    (rc >= 0) && ((tok = strtok_r(tok, "\r\n", &ptr)));
+                    tok = NULL) {
+                rc = al->log(tok);
+            }
+        }
+
         if (al->startListener()) {
             delete al;
             close(fdDmesg);