liblog: convert to C++

Bug: 119867234
Test: liblog, logcat, logd unit tests

Change-Id: I2a33b6fc37f9860c0041e3573bfb59ea389f623c
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
new file mode 100644
index 0000000..c50c7f7
--- /dev/null
+++ b/liblog/pmsg_writer.cpp
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2007-2016 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.
+ */
+
+/*
+ * pmsg write handler
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <log/log_properties.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "config_write.h"
+#include "log_portability.h"
+#include "logger.h"
+
+static int pmsgOpen();
+static void pmsgClose();
+static int pmsgAvailable(log_id_t logId);
+static int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
+
+LIBLOG_HIDDEN struct android_log_transport_write pmsgLoggerWrite = {
+    .node = {&pmsgLoggerWrite.node, &pmsgLoggerWrite.node},
+    .context.fd = -1,
+    .name = "pmsg",
+    .available = pmsgAvailable,
+    .open = pmsgOpen,
+    .close = pmsgClose,
+    .write = pmsgWrite,
+};
+
+static int pmsgOpen() {
+  int fd = atomic_load(&pmsgLoggerWrite.context.fd);
+  if (fd < 0) {
+    int i;
+
+    fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+    i = atomic_exchange(&pmsgLoggerWrite.context.fd, fd);
+    if ((i >= 0) && (i != fd)) {
+      close(i);
+    }
+  }
+
+  return fd;
+}
+
+static void pmsgClose() {
+  int fd = atomic_exchange(&pmsgLoggerWrite.context.fd, -1);
+  if (fd >= 0) {
+    close(fd);
+  }
+}
+
+static int pmsgAvailable(log_id_t logId) {
+  if (logId > LOG_ID_SECURITY) {
+    return -EINVAL;
+  }
+  if ((logId != LOG_ID_SECURITY) && (logId != LOG_ID_EVENTS) && !__android_log_is_debuggable()) {
+    return -EINVAL;
+  }
+  if (atomic_load(&pmsgLoggerWrite.context.fd) < 0) {
+    if (access("/dev/pmsg0", W_OK) == 0) {
+      return 0;
+    }
+    return -EBADF;
+  }
+  return 1;
+}
+
+/*
+ * Extract a 4-byte value from a byte stream.
+ */
+static inline uint32_t get4LE(const uint8_t* src) {
+  return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+static int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
+  static const unsigned headerLength = 2;
+  struct iovec newVec[nr + headerLength];
+  android_log_header_t header;
+  android_pmsg_log_header_t pmsgHeader;
+  size_t i, payloadSize;
+  ssize_t ret;
+
+  if ((logId == LOG_ID_EVENTS) && !__android_log_is_debuggable()) {
+    if (vec[0].iov_len < 4) {
+      return -EINVAL;
+    }
+
+    if (SNET_EVENT_LOG_TAG != get4LE(static_cast<uint8_t*>(vec[0].iov_base))) {
+      return -EPERM;
+    }
+  }
+
+  if (atomic_load(&pmsgLoggerWrite.context.fd) < 0) {
+    return -EBADF;
+  }
+
+  /*
+   *  struct {
+   *      // what we provide to pstore
+   *      android_pmsg_log_header_t pmsgHeader;
+   *      // what we provide to file
+   *      android_log_header_t header;
+   *      // caller provides
+   *      union {
+   *          struct {
+   *              char     prio;
+   *              char     payload[];
+   *          } string;
+   *          struct {
+   *              uint32_t tag
+   *              char     payload[];
+   *          } binary;
+   *      };
+   *  };
+   */
+
+  pmsgHeader.magic = LOGGER_MAGIC;
+  pmsgHeader.len = sizeof(pmsgHeader) + sizeof(header);
+  pmsgHeader.uid = __android_log_uid();
+  pmsgHeader.pid = getpid();
+
+  header.id = logId;
+  header.tid = gettid();
+  header.realtime.tv_sec = ts->tv_sec;
+  header.realtime.tv_nsec = ts->tv_nsec;
+
+  newVec[0].iov_base = (unsigned char*)&pmsgHeader;
+  newVec[0].iov_len = sizeof(pmsgHeader);
+  newVec[1].iov_base = (unsigned char*)&header;
+  newVec[1].iov_len = sizeof(header);
+
+  for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
+    newVec[i].iov_base = vec[i - headerLength].iov_base;
+    payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
+
+    if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {
+      newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;
+      if (newVec[i].iov_len) {
+        ++i;
+      }
+      payloadSize = LOGGER_ENTRY_MAX_PAYLOAD;
+      break;
+    }
+  }
+  pmsgHeader.len += payloadSize;
+
+  ret = TEMP_FAILURE_RETRY(writev(atomic_load(&pmsgLoggerWrite.context.fd), newVec, i));
+  if (ret < 0) {
+    ret = errno ? -errno : -ENOTCONN;
+  }
+
+  if (ret > (ssize_t)(sizeof(header) + sizeof(pmsgHeader))) {
+    ret -= sizeof(header) - sizeof(pmsgHeader);
+  }
+
+  return ret;
+}
+
+/*
+ * Virtual pmsg filesystem
+ *
+ * Payload will comprise the string "<basedir>:<basefile>\0<content>" to a
+ * maximum of LOGGER_ENTRY_MAX_PAYLOAD, but scaled to the last newline in the
+ * file.
+ *
+ * Will hijack the header.realtime.tv_nsec field for a sequence number in usec.
+ */
+
+static inline const char* strnrchr(const char* buf, size_t len, char c) {
+  const char* cp = buf + len;
+  while ((--cp > buf) && (*cp != c))
+    ;
+  if (cp <= buf) {
+    return buf + len;
+  }
+  return cp;
+}
+
+/* Write a buffer as filename references (tag = <basedir>:<basename>) */
+LIBLOG_ABI_PRIVATE ssize_t __android_log_pmsg_file_write(log_id_t logId, char prio,
+                                                         const char* filename, const char* buf,
+                                                         size_t len) {
+  bool weOpened;
+  size_t length, packet_len;
+  const char* tag;
+  char *cp, *slash;
+  struct timespec ts;
+  struct iovec vec[3];
+
+  /* Make sure the logId value is not a bad idea */
+  if ((logId == LOG_ID_KERNEL) ||   /* Verbotten */
+      (logId == LOG_ID_EVENTS) ||   /* Do not support binary content */
+      (logId == LOG_ID_SECURITY) || /* Bad idea to allow */
+      ((unsigned)logId >= 32)) {    /* fit within logMask on arch32 */
+    return -EINVAL;
+  }
+
+  clock_gettime(android_log_clockid(), &ts);
+
+  cp = strdup(filename);
+  if (!cp) {
+    return -ENOMEM;
+  }
+
+  tag = cp;
+  slash = strrchr(cp, '/');
+  if (slash) {
+    *slash = ':';
+    slash = strrchr(cp, '/');
+    if (slash) {
+      tag = slash + 1;
+    }
+  }
+
+  length = strlen(tag) + 1;
+  packet_len = LOGGER_ENTRY_MAX_PAYLOAD - sizeof(char) - length;
+
+  vec[0].iov_base = &prio;
+  vec[0].iov_len = sizeof(char);
+  vec[1].iov_base = (unsigned char*)tag;
+  vec[1].iov_len = length;
+
+  weOpened = false;
+  for (ts.tv_nsec = 0, length = len; length; ts.tv_nsec += ANDROID_LOG_PMSG_FILE_SEQUENCE) {
+    ssize_t ret;
+    size_t transfer;
+
+    if ((ts.tv_nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >= ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE) {
+      len -= length;
+      break;
+    }
+
+    transfer = length;
+    if (transfer > packet_len) {
+      transfer = strnrchr(buf, packet_len - 1, '\n') - buf;
+      if ((transfer < length) && (buf[transfer] == '\n')) {
+        ++transfer;
+      }
+    }
+
+    vec[2].iov_base = (unsigned char*)buf;
+    vec[2].iov_len = transfer;
+
+    if (atomic_load(&pmsgLoggerWrite.context.fd) < 0) {
+      if (!weOpened) { /* Impossible for weOpened = true here */
+        __android_log_lock();
+      }
+      weOpened = atomic_load(&pmsgLoggerWrite.context.fd) < 0;
+      if (!weOpened) {
+        __android_log_unlock();
+      } else if (pmsgOpen() < 0) {
+        __android_log_unlock();
+        free(cp);
+        return -EBADF;
+      }
+    }
+
+    ret = pmsgWrite(logId, &ts, vec, sizeof(vec) / sizeof(vec[0]));
+
+    if (ret <= 0) {
+      if (weOpened) {
+        pmsgClose();
+        __android_log_unlock();
+      }
+      free(cp);
+      return ret ? ret : (len - length);
+    }
+    length -= transfer;
+    buf += transfer;
+  }
+  if (weOpened) {
+    pmsgClose();
+    __android_log_unlock();
+  }
+  free(cp);
+  return len;
+}