diff options
Diffstat (limited to 'liblog/log_read.c')
-rw-r--r-- | liblog/log_read.c | 857 |
1 files changed, 458 insertions, 399 deletions
diff --git a/liblog/log_read.c b/liblog/log_read.c index 47aa711..ca5a1a7 100644 --- a/liblog/log_read.c +++ b/liblog/log_read.c @@ -14,37 +14,180 @@ ** limitations under the License. */ -#define _GNU_SOURCE /* asprintf for x86 host */ #include <errno.h> #include <fcntl.h> +#include <inttypes.h> #include <poll.h> -#include <string.h> -#include <stdio.h> +#include <signal.h> +#include <stddef.h> +#define NOMINMAX /* for windows to suppress definition of min in stdlib.h */ #include <stdlib.h> +#include <string.h> +#include <sys/cdefs.h> +#include <unistd.h> + #include <cutils/list.h> +#include <cutils/sockets.h> #include <log/log.h> #include <log/logger.h> -#include <sys/ioctl.h> +/* branchless on many architectures. */ +#define min(x,y) ((y) ^ (((x) ^ (y)) & -((x) < (y)))) + +#define WEAK __attribute__((weak)) +#ifndef __unused +#define __unused __attribute__((unused)) +#endif + +/* Private copy of ../libcutils/socket_local_client.c prevent library loops */ + +#ifdef HAVE_WINSOCK + +int WEAK socket_local_client(const char *name, int namespaceId, int type) +{ + errno = ENOSYS; + return -ENOSYS; +} + +#else /* !HAVE_WINSOCK */ + +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/select.h> +#include <sys/types.h> + +/* Private copy of ../libcutils/socket_local.h prevent library loops */ +#define FILESYSTEM_SOCKET_PREFIX "/tmp/" +#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/" +/* End of ../libcutils/socket_local.h */ + +#define LISTEN_BACKLOG 4 + +/* Documented in header file. */ +int WEAK socket_make_sockaddr_un(const char *name, int namespaceId, + struct sockaddr_un *p_addr, socklen_t *alen) +{ + memset (p_addr, 0, sizeof (*p_addr)); + size_t namelen; + + switch (namespaceId) { + case ANDROID_SOCKET_NAMESPACE_ABSTRACT: +#ifdef HAVE_LINUX_LOCAL_SOCKET_NAMESPACE + namelen = strlen(name); + + /* Test with length +1 for the *initial* '\0'. */ + if ((namelen + 1) > sizeof(p_addr->sun_path)) { + goto error; + } + + /* + * Note: The path in this case is *not* supposed to be + * '\0'-terminated. ("man 7 unix" for the gory details.) + */ + + p_addr->sun_path[0] = 0; + memcpy(p_addr->sun_path + 1, name, namelen); +#else /*HAVE_LINUX_LOCAL_SOCKET_NAMESPACE*/ + /* this OS doesn't have the Linux abstract namespace */ + + namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX); + /* unix_path_max appears to be missing on linux */ + if (namelen > sizeof(*p_addr) + - offsetof(struct sockaddr_un, sun_path) - 1) { + goto error; + } + + strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX); + strcat(p_addr->sun_path, name); +#endif /*HAVE_LINUX_LOCAL_SOCKET_NAMESPACE*/ + break; + + case ANDROID_SOCKET_NAMESPACE_RESERVED: + namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX); + /* unix_path_max appears to be missing on linux */ + if (namelen > sizeof(*p_addr) + - offsetof(struct sockaddr_un, sun_path) - 1) { + goto error; + } + + strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX); + strcat(p_addr->sun_path, name); + break; + + case ANDROID_SOCKET_NAMESPACE_FILESYSTEM: + namelen = strlen(name); + /* unix_path_max appears to be missing on linux */ + if (namelen > sizeof(*p_addr) + - offsetof(struct sockaddr_un, sun_path) - 1) { + goto error; + } + + strcpy(p_addr->sun_path, name); + break; + + default: + /* invalid namespace id */ + return -1; + } + + p_addr->sun_family = AF_LOCAL; + *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1; + return 0; +error: + return -1; +} + +/** + * connect to peer named "name" on fd + * returns same fd or -1 on error. + * fd is not closed on error. that's your job. + * + * Used by AndroidSocketImpl + */ +int WEAK socket_local_client_connect(int fd, const char *name, int namespaceId, + int type __unused) +{ + struct sockaddr_un addr; + socklen_t alen; + int err; + + err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen); + + if (err < 0) { + goto error; + } + + if(connect(fd, (struct sockaddr *) &addr, alen) < 0) { + goto error; + } + + return fd; -#define __LOGGERIO 0xAE +error: + return -1; +} + +/** + * connect to peer named "name" + * returns fd or -1 on error + */ +int WEAK socket_local_client(const char *name, int namespaceId, int type) +{ + int s; -#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */ -#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ -#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ -#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ -#define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */ -#define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */ + s = socket(AF_LOCAL, type, 0); + if(s < 0) return -1; -typedef char bool; -#define false (const bool)0 -#define true (const bool)1 + if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) { + close(s); + return -1; + } -#define LOG_FILE_DIR "/dev/log/" + return s; +} -/* timeout in milliseconds */ -#define LOG_TIMEOUT_FLUSH 5 -#define LOG_TIMEOUT_NEVER -1 +#endif /* !HAVE_WINSOCK */ +/* End of ../libcutils/socket_local_client.c */ #define logger_for_each(logger, logger_list) \ for (logger = node_to_item((logger_list)->node.next, struct logger, node); \ @@ -56,47 +199,21 @@ static const char *LOG_NAME[LOG_ID_MAX] = { [LOG_ID_MAIN] = "main", [LOG_ID_RADIO] = "radio", [LOG_ID_EVENTS] = "events", - [LOG_ID_SYSTEM] = "system" + [LOG_ID_SYSTEM] = "system", + [LOG_ID_CRASH] = "crash", }; -const char *android_log_id_to_name(log_id_t log_id) { +const char *android_log_id_to_name(log_id_t log_id) +{ if (log_id >= LOG_ID_MAX) { log_id = LOG_ID_MAIN; } return LOG_NAME[log_id]; } -static int accessmode(int mode) +log_id_t android_name_to_log_id(const char *logName) { - if ((mode & O_ACCMODE) == O_WRONLY) { - return W_OK; - } - if ((mode & O_ACCMODE) == O_RDWR) { - return R_OK | W_OK; - } - return R_OK; -} - -/* repeated fragment */ -static int check_allocate_accessible(char **n, const char *b, int mode) -{ - *n = NULL; - - if (!b) { - return -EINVAL; - } - - asprintf(n, LOG_FILE_DIR "%s", b); - if (!*n) { - return -1; - } - - return access(*n, accessmode(mode)); -} - -log_id_t android_name_to_log_id(const char *logName) { const char *b; - char *n; int ret; if (!logName) { @@ -109,12 +226,6 @@ log_id_t android_name_to_log_id(const char *logName) { ++b; } - ret = check_allocate_accessible(&n, b, O_RDONLY); - free(n); - if (ret) { - return ret; - } - for(ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) { const char *l = LOG_NAME[ret]; if (l && !strcmp(b, l)) { @@ -128,27 +239,15 @@ struct logger_list { struct listnode node; int mode; unsigned int tail; + log_time start; pid_t pid; - unsigned int queued_lines; - int timeout_ms; - int error; - bool flush; - bool valid_entry; /* valiant(?) effort to deal with memory starvation */ - struct log_msg entry; -}; - -struct log_list { - struct listnode node; - struct log_msg entry; /* Truncated to event->len() + 1 to save space */ + int sock; }; struct logger { struct listnode node; struct logger_list *top; - int fd; log_id_t id; - short *revents; - struct listnode log_list; }; /* android_logger_alloc unimplemented, no use case */ @@ -159,91 +258,209 @@ static void android_logger_free(struct logger *logger) return; } - while (!list_empty(&logger->log_list)) { - struct log_list *entry = node_to_item( - list_head(&logger->log_list), struct log_list, node); - list_remove(&entry->node); - free(entry); - if (logger->top->queued_lines) { - logger->top->queued_lines--; - } - } - - if (logger->fd >= 0) { - close(logger->fd); - } - list_remove(&logger->node); free(logger); } +/* android_logger_alloc unimplemented, no use case */ + +/* method for getting the associated sublog id */ log_id_t android_logger_get_id(struct logger *logger) { return logger->id; } /* worker for sending the command to the logger */ -static int logger_ioctl(struct logger *logger, int cmd, int mode) +static ssize_t send_log_msg(struct logger *logger, + const char *msg, char *buf, size_t buf_size) { - char *n; - int f, ret; - - if (!logger || !logger->top) { - return -EFAULT; + ssize_t ret; + size_t len; + char *cp; + int errno_save = 0; + int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM); + if (sock < 0) { + return sock; } - if (((mode & O_ACCMODE) == O_RDWR) - || (((mode ^ logger->top->mode) & O_ACCMODE) == 0)) { - return ioctl(logger->fd, cmd); + if (msg) { + snprintf(buf, buf_size, msg, logger ? logger->id : (unsigned) -1); } - /* We go here if android_logger_list_open got mode wrong for this ioctl */ - ret = check_allocate_accessible(&n, android_log_id_to_name(logger->id), mode); - if (ret) { - free(n); - return ret; + len = strlen(buf) + 1; + ret = TEMP_FAILURE_RETRY(write(sock, buf, len)); + if (ret <= 0) { + goto done; } - f = open(n, mode); - free(n); - if (f < 0) { - return f; + len = buf_size; + cp = buf; + while ((ret = TEMP_FAILURE_RETRY(read(sock, cp, len))) > 0) { + struct pollfd p; + + if (((size_t)ret == len) || (buf_size < PAGE_SIZE)) { + break; + } + + len -= ret; + cp += ret; + + memset(&p, 0, sizeof(p)); + p.fd = sock; + p.events = POLLIN; + + /* Give other side 20ms to refill pipe */ + ret = TEMP_FAILURE_RETRY(poll(&p, 1, 20)); + + if (ret <= 0) { + break; + } + + if (!(p.revents & POLLIN)) { + ret = 0; + break; + } } - ret = ioctl(f, cmd); - close (f); + if (ret >= 0) { + ret += buf_size - len; + } +done: + if ((ret == -1) && errno) { + errno_save = errno; + } + close(sock); + if (errno_save) { + errno = errno_save; + } return ret; } +static int check_log_success(char *buf, ssize_t ret) +{ + if (ret < 0) { + return ret; + } + + if (strncmp(buf, "success", 7)) { + errno = EINVAL; + return -1; + } + + return 0; +} + int android_logger_clear(struct logger *logger) { - return logger_ioctl(logger, LOGGER_FLUSH_LOG, O_WRONLY); + char buf[512]; + + return check_log_success(buf, + send_log_msg(logger, "clear %d", buf, sizeof(buf))); } /* returns the total size of the log's ring buffer */ -int android_logger_get_log_size(struct logger *logger) +long android_logger_get_log_size(struct logger *logger) +{ + char buf[512]; + + ssize_t ret = send_log_msg(logger, "getLogSize %d", buf, sizeof(buf)); + if (ret < 0) { + return ret; + } + + if ((buf[0] < '0') || ('9' < buf[0])) { + return -1; + } + + return atol(buf); +} + +int android_logger_set_log_size(struct logger *logger, unsigned long size) { - return logger_ioctl(logger, LOGGER_GET_LOG_BUF_SIZE, O_RDWR); + char buf[512]; + + snprintf(buf, sizeof(buf), "setLogSize %d %lu", + logger ? logger->id : (unsigned) -1, size); + + return check_log_success(buf, send_log_msg(NULL, NULL, buf, sizeof(buf))); } /* * returns the readable size of the log's ring buffer (that is, amount of the * log consumed) */ -int android_logger_get_log_readable_size(struct logger *logger) +long android_logger_get_log_readable_size(struct logger *logger) { - return logger_ioctl(logger, LOGGER_GET_LOG_LEN, O_RDONLY); + char buf[512]; + + ssize_t ret = send_log_msg(logger, "getLogSizeUsed %d", buf, sizeof(buf)); + if (ret < 0) { + return ret; + } + + if ((buf[0] < '0') || ('9' < buf[0])) { + return -1; + } + + return atol(buf); } /* * returns the logger version */ -int android_logger_get_log_version(struct logger *logger) +int android_logger_get_log_version(struct logger *logger __unused) +{ + return 3; +} + +/* + * returns statistics + */ +ssize_t android_logger_get_statistics(struct logger_list *logger_list, + char *buf, size_t len) +{ + struct logger *logger; + char *cp = buf; + size_t remaining = len; + size_t n; + + n = snprintf(cp, remaining, "getStatistics"); + n = min(n, remaining); + remaining -= n; + cp += n; + + logger_for_each(logger, logger_list) { + n = snprintf(cp, remaining, " %d", logger->id); + n = min(n, remaining); + remaining -= n; + cp += n; + } + return send_log_msg(NULL, NULL, buf, len); +} + +ssize_t android_logger_get_prune_list(struct logger_list *logger_list __unused, + char *buf, size_t len) +{ + return send_log_msg(NULL, "getPruneList", buf, len); +} + +int android_logger_set_prune_list(struct logger_list *logger_list __unused, + char *buf, size_t len) { - int ret = logger_ioctl(logger, LOGGER_GET_VERSION, O_RDWR); - return (ret < 0) ? 1 : ret; + const char cmd[] = "setPruneList "; + const size_t cmdlen = sizeof(cmd) - 1; + + if (strlen(buf) > (len - cmdlen)) { + return -ENOMEM; /* KISS */ + } + memmove(buf + cmdlen, buf, len - cmdlen); + buf[len - 1] = '\0'; + memcpy(buf, cmd, cmdlen); + + return check_log_success(buf, send_log_msg(NULL, NULL, buf, len)); } struct logger_list *android_logger_list_alloc(int mode, @@ -256,10 +473,36 @@ struct logger_list *android_logger_list_alloc(int mode, if (!logger_list) { return NULL; } + list_init(&logger_list->node); logger_list->mode = mode; + logger_list->start.tv_sec = 0; + logger_list->start.tv_nsec = 0; logger_list->tail = tail; logger_list->pid = pid; + logger_list->sock = -1; + + return logger_list; +} + +struct logger_list *android_logger_list_alloc_time(int mode, + log_time start, + pid_t pid) +{ + struct logger_list *logger_list; + + logger_list = calloc(1, sizeof(*logger_list)); + if (!logger_list) { + return NULL; + } + + list_init(&logger_list->node); + logger_list->mode = mode; + logger_list->start = start; + logger_list->tail = 0; + logger_list->pid = pid; + logger_list->sock = -1; + return logger_list; } @@ -270,9 +513,7 @@ struct logger_list *android_logger_list_alloc(int mode, struct logger *android_logger_open(struct logger_list *logger_list, log_id_t id) { - struct listnode *node; struct logger *logger; - char *n; if (!logger_list || (id >= LOG_ID_MAX)) { goto err; @@ -289,28 +530,11 @@ struct logger *android_logger_open(struct logger_list *logger_list, goto err; } - if (check_allocate_accessible(&n, android_log_id_to_name(id), - logger_list->mode)) { - goto err_name; - } - - logger->fd = open(n, logger_list->mode); - if (logger->fd < 0) { - goto err_name; - } - - free(n); logger->id = id; - list_init(&logger->log_list); list_add_tail(&logger_list->node, &logger->node); logger->top = logger_list; - logger_list->timeout_ms = LOG_TIMEOUT_FLUSH; goto ok; -err_name: - free(n); -err_logger: - free(logger); err: logger = NULL; ok: @@ -336,315 +560,146 @@ struct logger_list *android_logger_list_open(log_id_t id, return logger_list; } -/* prevent memory starvation when backfilling */ -static unsigned int queue_threshold(struct logger_list *logger_list) -{ - return (logger_list->tail < 64) ? 64 : logger_list->tail; -} - -static bool low_queue(struct listnode *node) -{ - /* low is considered less than 2 */ - return list_head(node) == list_tail(node); -} - -/* Flush queues in sequential order, one at a time */ -static int android_logger_list_flush(struct logger_list *logger_list, - struct log_msg *log_msg) +static void caught_signal(int signum __unused) { - int ret = 0; - struct log_list *firstentry = NULL; - - while ((ret == 0) - && (logger_list->flush - || (logger_list->queued_lines > logger_list->tail))) { - struct logger *logger; - - /* Merge sort */ - bool at_least_one_is_low = false; - struct logger *firstlogger = NULL; - firstentry = NULL; - - logger_for_each(logger, logger_list) { - struct listnode *node; - struct log_list *oldest = NULL; - - /* kernel logger channels not necessarily time-sort order */ - list_for_each(node, &logger->log_list) { - struct log_list *entry = node_to_item(node, - struct log_list, node); - if (!oldest - || (entry->entry.entry.sec < oldest->entry.entry.sec) - || ((entry->entry.entry.sec == oldest->entry.entry.sec) - && (entry->entry.entry.nsec < oldest->entry.entry.nsec))) { - oldest = entry; - } - } - - if (!oldest) { - at_least_one_is_low = true; - continue; - } else if (low_queue(&logger->log_list)) { - at_least_one_is_low = true; - } - - if (!firstentry - || (oldest->entry.entry.sec < firstentry->entry.entry.sec) - || ((oldest->entry.entry.sec == firstentry->entry.entry.sec) - && (oldest->entry.entry.nsec < firstentry->entry.entry.nsec))) { - firstentry = oldest; - firstlogger = logger; - } - } - - if (!firstentry) { - break; - } - - /* when trimming list, tries to keep one entry behind in each bucket */ - if (!logger_list->flush - && at_least_one_is_low - && (logger_list->queued_lines < queue_threshold(logger_list))) { - break; - } - - /* within tail?, send! */ - if ((logger_list->tail == 0) - || (logger_list->queued_lines <= logger_list->tail)) { - ret = firstentry->entry.entry.hdr_size; - if (!ret) { - ret = sizeof(firstentry->entry.entry_v1); - } - ret += firstentry->entry.entry.len; - - memcpy(log_msg->buf, firstentry->entry.buf, ret + 1); - log_msg->extra.id = firstlogger->id; - } - - /* next entry */ - list_remove(&firstentry->node); - free(firstentry); - if (logger_list->queued_lines) { - logger_list->queued_lines--; - } - } - - /* Flushed the list, no longer in tail mode for continuing content */ - if (logger_list->flush && !firstentry) { - logger_list->tail = 0; - } - return ret; } /* Read from the selected logs */ int android_logger_list_read(struct logger_list *logger_list, struct log_msg *log_msg) { + int ret, e; struct logger *logger; - nfds_t nfds; - struct pollfd *p, *pollfds = NULL; - int error = 0, ret = 0; - - memset(log_msg, 0, sizeof(struct log_msg)); + struct sigaction ignore; + struct sigaction old_sigaction; + unsigned int old_alarm = 0; if (!logger_list) { - return -ENODEV; - } - - if (!(accessmode(logger_list->mode) & R_OK)) { - logger_list->error = EPERM; - goto done; - } - - nfds = 0; - logger_for_each(logger, logger_list) { - ++nfds; - } - if (nfds <= 0) { - error = ENODEV; - goto done; - } - - /* Do we have anything to offer from the buffer or state? */ - if (logger_list->valid_entry) { /* implies we are also in a flush state */ - goto flush; - } - - ret = android_logger_list_flush(logger_list, log_msg); - if (ret) { - goto done; + return -EINVAL; } - if (logger_list->error) { /* implies we are also in a flush state */ - goto done; + if (logger_list->mode & O_NONBLOCK) { + memset(&ignore, 0, sizeof(ignore)); + ignore.sa_handler = caught_signal; + sigemptyset(&ignore.sa_mask); } - /* Lets start grinding on metal */ - pollfds = calloc(nfds, sizeof(struct pollfd)); - if (!pollfds) { - error = ENOMEM; - goto flush; - } + if (logger_list->sock < 0) { + char buffer[256], *cp, c; - p = pollfds; - logger_for_each(logger, logger_list) { - p->fd = logger->fd; - p->events = POLLIN; - logger->revents = &p->revents; - ++p; - } - - while (!ret && !error) { - int result; - - /* If we oversleep it's ok, i.e. ignore EINTR. */ - result = TEMP_FAILURE_RETRY( - poll(pollfds, nfds, logger_list->timeout_ms)); - - if (result <= 0) { - if (result) { - error = errno; - } else if (logger_list->mode & O_NDELAY) { - error = EAGAIN; - } else { - logger_list->timeout_ms = LOG_TIMEOUT_NEVER; + int sock = socket_local_client("logdr", + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_SEQPACKET); + if (sock < 0) { + if ((sock == -1) && errno) { + return -errno; } - - logger_list->flush = true; - goto try_flush; + return sock; } - logger_list->timeout_ms = LOG_TIMEOUT_FLUSH; - - /* Anti starvation */ - if (!logger_list->flush - && (logger_list->queued_lines > (queue_threshold(logger_list) / 2))) { - /* Any queues with input pending that is low? */ - bool starving = false; - logger_for_each(logger, logger_list) { - if ((*(logger->revents) & POLLIN) - && low_queue(&logger->log_list)) { - starving = true; - break; - } - } + strcpy(buffer, + (logger_list->mode & O_NONBLOCK) ? "dumpAndClose" : "stream"); + cp = buffer + strlen(buffer); - /* pushback on any queues that are not low */ - if (starving) { - logger_for_each(logger, logger_list) { - if ((*(logger->revents) & POLLIN) - && !low_queue(&logger->log_list)) { - *(logger->revents) &= ~POLLIN; - } - } - } + strcpy(cp, " lids"); + cp += 5; + c = '='; + int remaining = sizeof(buffer) - (cp - buffer); + logger_for_each(logger, logger_list) { + ret = snprintf(cp, remaining, "%c%u", c, logger->id); + ret = min(ret, remaining); + remaining -= ret; + cp += ret; + c = ','; } - logger_for_each(logger, logger_list) { - unsigned int hdr_size; - struct log_list *entry; + if (logger_list->tail) { + ret = snprintf(cp, remaining, " tail=%u", logger_list->tail); + ret = min(ret, remaining); + remaining -= ret; + cp += ret; + } - if (!(*(logger->revents) & POLLIN)) { - continue; - } + if (logger_list->start.tv_sec || logger_list->start.tv_nsec) { + ret = snprintf(cp, remaining, " start=%" PRIu32 ".%09" PRIu32, + logger_list->start.tv_sec, + logger_list->start.tv_nsec); + ret = min(ret, remaining); + remaining -= ret; + cp += ret; + } - memset(logger_list->entry.buf, 0, sizeof(struct log_msg)); - /* NOTE: driver guarantees we read exactly one full entry */ - result = read(logger->fd, logger_list->entry.buf, - LOGGER_ENTRY_MAX_LEN); - if (result <= 0) { - if (!result) { - error = EIO; - } else if (errno != EINTR) { - error = errno; - } - continue; - } + if (logger_list->pid) { + ret = snprintf(cp, remaining, " pid=%u", logger_list->pid); + ret = min(ret, remaining); + remaining -= ret; + cp += ret; + } - if (logger_list->pid - && (logger_list->pid != logger_list->entry.entry.pid)) { - continue; + if (logger_list->mode & O_NONBLOCK) { + /* Deal with an unresponsive logd */ + sigaction(SIGALRM, &ignore, &old_sigaction); + old_alarm = alarm(30); + } + ret = write(sock, buffer, cp - buffer); + e = errno; + if (logger_list->mode & O_NONBLOCK) { + if (e == EINTR) { + e = ETIMEDOUT; } + alarm(old_alarm); + sigaction(SIGALRM, &old_sigaction, NULL); + } - hdr_size = logger_list->entry.entry.hdr_size; - if (!hdr_size) { - hdr_size = sizeof(logger_list->entry.entry_v1); + if (ret <= 0) { + close(sock); + if ((ret == -1) && e) { + return -e; } - - if ((hdr_size > sizeof(struct log_msg)) - || (logger_list->entry.entry.len - > sizeof(logger_list->entry.buf) - hdr_size) - || (logger_list->entry.entry.len != result - hdr_size)) { - error = EINVAL; - continue; + if (ret == 0) { + return -EIO; } + return ret; + } - logger_list->entry.extra.id = logger->id; - - /* speedup: If not tail, and only one list, send directly */ - if (!logger_list->tail - && (list_head(&logger_list->node) - == list_tail(&logger_list->node))) { - ret = result; - memcpy(log_msg->buf, logger_list->entry.buf, result + 1); - log_msg->extra.id = logger->id; - break; - } + logger_list->sock = sock; + } - entry = malloc(sizeof(*entry) - sizeof(entry->entry) + result + 1); + ret = 0; + while(1) { + memset(log_msg, 0, sizeof(*log_msg)); - if (!entry) { - logger_list->valid_entry = true; - error = ENOMEM; - break; + if (logger_list->mode & O_NONBLOCK) { + /* particularily useful if tombstone is reporting for logd */ + sigaction(SIGALRM, &ignore, &old_sigaction); + old_alarm = alarm(30); + } + /* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */ + ret = recv(logger_list->sock, log_msg, LOGGER_ENTRY_MAX_LEN, 0); + e = errno; + if (logger_list->mode & O_NONBLOCK) { + if ((ret == 0) || (e == EINTR)) { + e = EAGAIN; + ret = -1; } - - logger_list->queued_lines++; - - memcpy(entry->entry.buf, logger_list->entry.buf, result); - entry->entry.buf[result] = '\0'; - list_add_tail(&logger->log_list, &entry->node); + alarm(old_alarm); + sigaction(SIGALRM, &old_sigaction, NULL); } if (ret <= 0) { -try_flush: - ret = android_logger_list_flush(logger_list, log_msg); - } - } - - free(pollfds); - -flush: - if (error) { - logger_list->flush = true; - } - - if (ret <= 0) { - ret = android_logger_list_flush(logger_list, log_msg); - - if (!ret && logger_list->valid_entry) { - ret = logger_list->entry.entry.hdr_size; - if (!ret) { - ret = sizeof(logger_list->entry.entry_v1); + if ((ret == -1) && e) { + return -e; } - ret += logger_list->entry.entry.len; - - memcpy(log_msg->buf, logger_list->entry.buf, - sizeof(struct log_msg)); - logger_list->valid_entry = false; + return ret; } - } -done: - if (logger_list->error) { - error = logger_list->error; - } - if (error) { - logger_list->error = error; - if (!ret) { - ret = -error; + logger_for_each(logger, logger_list) { + if (log_msg->entry.lid == logger->id) { + return ret; + } } } + /* NOTREACH */ return ret; } @@ -661,5 +716,9 @@ void android_logger_list_free(struct logger_list *logger_list) android_logger_free(logger); } + if (logger_list->sock >= 0) { + close (logger_list->sock); + } + free(logger_list); } |