diff --git a/include/private/android_logger.h b/include/private/android_logger.h index 04238a6fd..c3ea1ed7b 100644 --- a/include/private/android_logger.h +++ b/include/private/android_logger.h @@ -19,7 +19,10 @@ #ifndef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_ #define _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_ +/* Android private interfaces */ + #include +#include #include #include @@ -95,4 +98,35 @@ typedef struct __attribute__((__packed__)) { char data[]; } android_log_event_string_t; +#if defined(__cplusplus) +extern "C" { #endif + +#define ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE 256 /* 1MB file */ +#define ANDROID_LOG_PMSG_FILE_SEQUENCE 1000 + +ssize_t __android_log_pmsg_file_write( + log_id_t logId, + char prio, + const char *filename, + const char *buf, size_t len); + +#define LOG_ID_ANY ((log_id_t)-1) +#define ANDROID_LOG_ANY ANDROID_LOG_UNKNOWN + +/* first 5 arguments match __android_log_msg_file_write, a cast is safe */ +typedef ssize_t (*__android_log_pmsg_file_read_fn)( + log_id_t logId, + char prio, + const char *filename, + const char *buf, size_t len, void *arg); + +ssize_t __android_log_pmsg_file_read( + log_id_t logId, char prio, const char *prefix, + __android_log_pmsg_file_read_fn fn, void *arg); + +#if defined(__cplusplus) +} +#endif + +#endif /* _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_ */ diff --git a/liblog/Android.mk b/liblog/Android.mk index 6d53a4af0..01c8e77b2 100644 --- a/liblog/Android.mk +++ b/liblog/Android.mk @@ -24,10 +24,14 @@ include $(CLEAR_VARS) # so make sure we do not regret hard-coding it as follows: liblog_cflags := -DLIBLOG_LOG_TAG=1005 -liblog_sources := logd_write.c log_event_list.c log_event_write.c +liblog_sources := log_event_list.c log_event_write.c logger_write.c +liblog_sources += config_write.c logger_name.c logger_lock.c liblog_host_sources := $(liblog_sources) fake_log_device.c event.logtags +liblog_host_sources += fake_writer.c liblog_target_sources := $(liblog_sources) event_tag_map.c -liblog_target_sources += log_time.cpp log_is_loggable.c logprint.c log_read.c +liblog_target_sources += config_read.c log_time.cpp log_is_loggable.c logprint.c +liblog_target_sources += pmsg_reader.c pmsg_writer.c +liblog_target_sources += logd_reader.c logd_writer.c logger_read.c # Shared and static library for host # ======================================================== diff --git a/liblog/config_read.c b/liblog/config_read.c new file mode 100644 index 000000000..1f54152b0 --- /dev/null +++ b/liblog/config_read.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 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. + */ + +#include "config_read.h" +#include "logger.h" + +LIBLOG_HIDDEN struct listnode __android_log_transport_read = + { &__android_log_transport_read, &__android_log_transport_read }; +LIBLOG_HIDDEN struct listnode __android_log_persist_read = + { &__android_log_persist_read, &__android_log_persist_read }; + +static void __android_log_add_transport( + struct listnode *list, struct android_log_transport_read *transport) { + size_t i; + + /* Try to keep one functioning transport for each log buffer id */ + for (i = LOG_ID_MIN; i < LOG_ID_MAX; i++) { + struct android_log_transport_read *transp; + + if (list_empty(list)) { + if (!transport->available || ((*transport->available)(i) >= 0)) { + list_add_tail(list, &transport->node); + return; + } + } else { + read_transport_for_each(transp, list) { + if (!transp->available) { + return; + } + if (((*transp->available)(i) < 0) && + (!transport->available || + ((*transport->available)(i) >= 0))) { + list_add_tail(list, &transport->node); + return; + } + } + } + } +} + +LIBLOG_HIDDEN void __android_log_config_read() { +#if (FAKE_LOG_DEVICE == 0) + extern struct android_log_transport_read logdLoggerRead; + extern struct android_log_transport_read pmsgLoggerRead; + + __android_log_add_transport(&__android_log_transport_read, &logdLoggerRead); + __android_log_add_transport(&__android_log_persist_read, &pmsgLoggerRead); +#endif +} diff --git a/liblog/config_read.h b/liblog/config_read.h new file mode 100644 index 000000000..67f4c20d0 --- /dev/null +++ b/liblog/config_read.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 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. + */ + +#ifndef _LIBLOG_CONFIG_READ_H__ +#define _LIBLOG_CONFIG_READ_H__ + +#include + +#include "log_portability.h" + +__BEGIN_DECLS + +extern LIBLOG_HIDDEN struct listnode __android_log_transport_read; +extern LIBLOG_HIDDEN struct listnode __android_log_persist_read; + +#define read_transport_for_each(transp, transports) \ + for (transp = node_to_item((transports)->next, \ + struct android_log_transport_read, node); \ + (transp != node_to_item(transports, \ + struct android_log_transport_read, node)); \ + transp = node_to_item(transp->node.next, \ + struct android_log_transport_read, node)) \ + +#define read_transport_for_each_safe(transp, n, transports) \ + for (transp = node_to_item((transports)->next, \ + struct android_log_transport_read, node), \ + n = transp->node.next; \ + (transp != node_to_item(transports, \ + struct android_log_transport_read, node)); \ + transp = node_to_item(n, struct android_log_transport_read, node), \ + n = transp->node.next) + +LIBLOG_HIDDEN void __android_log_config_read(); + +__END_DECLS + +#endif /* _LIBLOG_CONFIG_READ_H__ */ diff --git a/liblog/config_write.c b/liblog/config_write.c new file mode 100644 index 000000000..d689f631b --- /dev/null +++ b/liblog/config_write.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 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. + */ + +#include "config_write.h" +#include "logger.h" + +LIBLOG_HIDDEN struct listnode __android_log_transport_write = + { &__android_log_transport_write, &__android_log_transport_write }; +LIBLOG_HIDDEN struct listnode __android_log_persist_write = + { &__android_log_persist_write, &__android_log_persist_write}; + +static void __android_log_add_transport( + struct listnode *list, struct android_log_transport_write *transport) { + size_t i; + + /* Try to keep one functioning transport for each log buffer id */ + for (i = LOG_ID_MIN; i < LOG_ID_MAX; i++) { + struct android_log_transport_write *transp; + + if (list_empty(list)) { + if (!transport->available || ((*transport->available)(i) >= 0)) { + list_add_tail(list, &transport->node); + return; + } + } else { + write_transport_for_each(transp, list) { + if (!transp->available) { + return; + } + if (((*transp->available)(i) < 0) && + (!transport->available || + ((*transport->available)(i) >= 0))) { + list_add_tail(list, &transport->node); + return; + } + } + } + } +} + +LIBLOG_HIDDEN void __android_log_config_write() { +#if (FAKE_LOG_DEVICE == 0) + extern struct android_log_transport_write logdLoggerWrite; + extern struct android_log_transport_write pmsgLoggerWrite; + + __android_log_add_transport(&__android_log_transport_write, &logdLoggerWrite); + __android_log_add_transport(&__android_log_persist_write, &pmsgLoggerWrite); +#else + extern struct android_log_transport_write fakeLoggerWrite; + + __android_log_add_transport(&__android_log_transport_write, &fakeLoggerWrite); +#endif +} diff --git a/liblog/config_write.h b/liblog/config_write.h new file mode 100644 index 000000000..3a02a4e79 --- /dev/null +++ b/liblog/config_write.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 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. + */ + +#ifndef _LIBLOG_CONFIG_WRITE_H__ +#define _LIBLOG_CONFIG_WRITE_H__ + +#include + +#include "log_portability.h" + +__BEGIN_DECLS + +extern LIBLOG_HIDDEN struct listnode __android_log_transport_write; +extern LIBLOG_HIDDEN struct listnode __android_log_persist_write; + +#define write_transport_for_each(transp, transports) \ + for (transp = node_to_item((transports)->next, \ + struct android_log_transport_write, node); \ + (transp != node_to_item(transports, \ + struct android_log_transport_write, node)); \ + transp = node_to_item(transp->node.next, \ + struct android_log_transport_write, node)) \ + +#define write_transport_for_each_safe(transp, n, transports) \ + for (transp = node_to_item((transports)->next, \ + struct android_log_transport_write, node), \ + n = transp->node.next; \ + (transp != node_to_item(transports, \ + struct android_log_transport_write, node)); \ + transp = node_to_item(n, struct android_log_transport_write, node), \ + n = transp->node.next) + +LIBLOG_HIDDEN void __android_log_config_write(); + +__END_DECLS + +#endif /* _LIBLOG_CONFIG_WRITE_H__ */ diff --git a/liblog/event_tag_map.c b/liblog/event_tag_map.c index 870c69aa2..64d872a25 100644 --- a/liblog/event_tag_map.c +++ b/liblog/event_tag_map.c @@ -24,7 +24,7 @@ #include #include -#include "log_cdefs.h" +#include "log_portability.h" #define OUT_TAG "EventTagMap" diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c index c73e03e21..cc67f3eba 100644 --- a/liblog/fake_log_device.c +++ b/liblog/fake_log_device.c @@ -31,7 +31,7 @@ #include #include "fake_log_device.h" -#include "log_cdefs.h" +#include "log_portability.h" #define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */ diff --git a/liblog/fake_log_device.h b/liblog/fake_log_device.h index 672b446f3..4529b5d95 100644 --- a/liblog/fake_log_device.h +++ b/liblog/fake_log_device.h @@ -19,7 +19,7 @@ #include -#include "log_cdefs.h" +#include "log_portability.h" struct iovec; diff --git a/liblog/fake_writer.c b/liblog/fake_writer.c new file mode 100644 index 000000000..dab8bc54e --- /dev/null +++ b/liblog/fake_writer.c @@ -0,0 +1,82 @@ +/* + * 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. + */ + +#include +#include +#include + +#include + +#include "config_write.h" +#include "fake_log_device.h" +#include "log_portability.h" +#include "logger.h" + +static int fakeOpen(); +static void fakeClose(); +static int fakeWrite(log_id_t log_id, struct timespec *ts, + struct iovec *vec, size_t nr); + +static int logFds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1, -1 }; + +LIBLOG_HIDDEN struct android_log_transport_write fakeLoggerWrite = { + .node = { &fakeLoggerWrite.node, &fakeLoggerWrite.node }, + .context.private = &logFds, + .name = "fake", + .available = NULL, + .open = fakeOpen, + .close = fakeClose, + .write = fakeWrite, +}; + +static int fakeOpen() { + int i; + + for (i = 0; i < LOG_ID_MAX; i++) { + char buf[sizeof("/dev/log_security")]; + snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i)); + logFds[i] = fakeLogOpen(buf, O_WRONLY); + } + return 0; +} + +static void fakeClose() { + int i; + + for (i = 0; i < LOG_ID_MAX; i++) { + fakeLogClose(logFds[i]); + logFds[i] = -1; + } +} + +static int fakeWrite(log_id_t log_id, struct timespec *ts __unused, + struct iovec *vec, size_t nr) +{ + ssize_t ret; + int logFd; + + if (/*(int)log_id >= 0 &&*/ (int)log_id >= (int)LOG_ID_MAX) { + return -EBADF; + } + + logFd = logFds[(int)log_id]; + ret = TEMP_FAILURE_RETRY(fakeLogWritev(logFd, vec, nr)); + if (ret < 0) { + ret = -errno; + } + + return ret; +} diff --git a/liblog/log_event_list.c b/liblog/log_event_list.c index a77c56eb1..64d9024c6 100644 --- a/liblog/log_event_list.c +++ b/liblog/log_event_list.c @@ -25,7 +25,7 @@ #include #include -#include "log_cdefs.h" +#include "log_portability.h" #define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t)) diff --git a/liblog/log_event_write.c b/liblog/log_event_write.c index 3535b94c1..b9827a16d 100644 --- a/liblog/log_event_write.c +++ b/liblog/log_event_write.c @@ -18,7 +18,7 @@ #include -#include "log_cdefs.h" +#include "log_portability.h" #define MAX_SUBTAG_LEN 32 diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c index 47fde204e..551fa7684 100644 --- a/liblog/log_is_loggable.c +++ b/liblog/log_is_loggable.c @@ -23,7 +23,7 @@ #include -#include "log_cdefs.h" +#include "log_portability.h" static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER; diff --git a/liblog/log_cdefs.h b/liblog/log_portability.h similarity index 65% rename from liblog/log_cdefs.h rename to liblog/log_portability.h index 3a526256e..3ad20601e 100644 --- a/liblog/log_cdefs.h +++ b/liblog/log_portability.h @@ -14,10 +14,13 @@ * limitations under the License. */ -#ifndef _LIBLOG_CDEFS_H__ -#define _LIBLOG_CDEFS_H__ +#ifndef _LIBLOG_PORTABILITY_H__ +#define _LIBLOG_PORTABILITY_H__ #include +#include + +/* Helpful private sys/cdefs.h like definitions */ /* Declare this library function hidden and internal */ #if defined(_WIN32) @@ -46,9 +49,34 @@ #define LIBLOG_WEAK __attribute__((weak,visibility("default"))) #endif +/* possible missing definitions in sys/cdefs.h */ + +/* DECLS */ +#ifndef __BEGIN_DECLS +#if defined(__cplusplus) +#define __BEGIN_DECLS extern "C" { +#define __END_DECLS } +#else +#define __BEGIN_DECLS +#define __END_DECLS +#endif +#endif + /* Unused argument. For C code only, remove symbol name for C++ */ #ifndef __unused #define __unused __attribute__((__unused__)) #endif -#endif /* _LIBLOG_CDEFS_H__ */ +/* possible missing definitions in unistd.h */ + +#ifndef TEMP_FAILURE_RETRY +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) ({ \ + __typeof__(exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) +#endif + +#endif /* _LIBLOG_PORTABILITY_H__ */ diff --git a/liblog/log_read.c b/liblog/log_read.c deleted file mode 100644 index 4b839443d..000000000 --- a/liblog/log_read.c +++ /dev/null @@ -1,917 +0,0 @@ -/* -** Copyright 2013-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 -#include -#include -#include -#include -#include -#include -#define NOMINMAX /* for windows to suppress definition of min in stdlib.h */ -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "log_cdefs.h" - -/* branchless on many architectures. */ -#define min(x,y) ((y) ^ (((x) ^ (y)) & -((x) < (y)))) - -/* Private copy of ../libcutils/socket_local_client.c prevent library loops */ - -#if defined(_WIN32) - -LIBLOG_WEAK int socket_local_client(const char *name, int namespaceId, int type) -{ - errno = ENOSYS; - return -ENOSYS; -} - -#else /* !_WIN32 */ - -#include -#include -#include -#include - -/* 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. */ -LIBLOG_WEAK int 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: -#if defined(__linux__) - 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 - /* 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 - 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 - */ -LIBLOG_WEAK int 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; - -error: - return -1; -} - -/** - * connect to peer named "name" - * returns fd or -1 on error - */ -LIBLOG_WEAK int socket_local_client(const char *name, int namespaceId, int type) -{ - int s; - - s = socket(AF_LOCAL, type, 0); - if(s < 0) return -1; - - if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) { - close(s); - return -1; - } - - return s; -} - -#endif /* !_WIN32 */ -/* 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); \ - logger != node_to_item(&(logger_list)->node, struct logger, node); \ - logger = node_to_item((logger)->node.next, struct logger, node)) - -/* In the future, we would like to make this list extensible */ -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_CRASH] = "crash", - [LOG_ID_SECURITY] = "security", - [LOG_ID_KERNEL] = "kernel", -}; - -LIBLOG_ABI_PUBLIC 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]; -} - -LIBLOG_ABI_PUBLIC log_id_t android_name_to_log_id(const char *logName) -{ - const char *b; - int ret; - - if (!logName) { - return -1; /* NB: log_id_t is unsigned */ - } - b = strrchr(logName, '/'); - if (!b) { - b = logName; - } else { - ++b; - } - - for(ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) { - const char *l = LOG_NAME[ret]; - if (l && !strcmp(b, l)) { - return ret; - } - } - return -1; /* should never happen */ -} - -struct logger_list { - struct listnode node; - int mode; - unsigned int tail; - log_time start; - pid_t pid; - int sock; -}; - -struct logger { - struct listnode node; - struct logger_list *top; - log_id_t id; -}; - -/* android_logger_alloc unimplemented, no use case */ -/* android_logger_free not exported */ -static void android_logger_free(struct logger *logger) -{ - if (!logger) { - return; - } - - list_remove(&logger->node); - - free(logger); -} - -/* android_logger_alloc unimplemented, no use case */ - -/* method for getting the associated sublog id */ -LIBLOG_ABI_PUBLIC log_id_t android_logger_get_id(struct logger *logger) -{ - return logger->id; -} - -/* worker for sending the command to the logger */ -static ssize_t send_log_msg(struct logger *logger, - const char *msg, char *buf, size_t buf_size) -{ - 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 (msg) { - snprintf(buf, buf_size, msg, logger ? logger->id : (unsigned) -1); - } - - len = strlen(buf) + 1; - ret = TEMP_FAILURE_RETRY(write(sock, buf, len)); - if (ret <= 0) { - goto done; - } - - 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; - } - } - - 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; -} - -/* Determine the credentials of the caller */ -static bool uid_has_log_permission(uid_t uid) -{ - return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT); -} - -static uid_t get_best_effective_uid() -{ - uid_t euid; - uid_t uid; - gid_t gid; - ssize_t i; - static uid_t last_uid = (uid_t) -1; - - if (last_uid != (uid_t) -1) { - return last_uid; - } - uid = getuid(); - if (uid_has_log_permission(uid)) { - return last_uid = uid; - } - euid = geteuid(); - if (uid_has_log_permission(euid)) { - return last_uid = euid; - } - gid = getgid(); - if (uid_has_log_permission(gid)) { - return last_uid = gid; - } - gid = getegid(); - if (uid_has_log_permission(gid)) { - return last_uid = gid; - } - i = getgroups((size_t) 0, NULL); - if (i > 0) { - gid_t list[i]; - - getgroups(i, list); - while (--i >= 0) { - if (uid_has_log_permission(list[i])) { - return last_uid = list[i]; - } - } - } - return last_uid = uid; -} - -LIBLOG_ABI_PUBLIC int android_logger_clear(struct logger *logger) -{ - char buf[512]; - - if (logger->top->mode & ANDROID_LOG_PSTORE) { - if (uid_has_log_permission(get_best_effective_uid())) { - return unlink("/sys/fs/pstore/pmsg-ramoops-0"); - } - errno = EPERM; - return -1; - } - return check_log_success(buf, - send_log_msg(logger, "clear %d", buf, sizeof(buf))); -} - -/* returns the total size of the log's ring buffer */ -LIBLOG_ABI_PUBLIC 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); -} - -LIBLOG_ABI_PUBLIC int android_logger_set_log_size(struct logger *logger, - unsigned long size) -{ - 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) - */ -LIBLOG_ABI_PUBLIC long android_logger_get_log_readable_size( - struct logger *logger) -{ - 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 - */ -LIBLOG_ABI_PUBLIC int android_logger_get_log_version( - struct logger *logger __unused) -{ - return 4; -} - -/* - * returns statistics - */ -LIBLOG_ABI_PUBLIC 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; - } - - if (logger_list->pid) { - snprintf(cp, remaining, " pid=%u", logger_list->pid); - } - - return send_log_msg(NULL, NULL, buf, len); -} - -LIBLOG_ABI_PUBLIC 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); -} - -LIBLOG_ABI_PUBLIC int android_logger_set_prune_list( - struct logger_list *logger_list __unused, - char *buf, size_t len) -{ - 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)); -} - -LIBLOG_ABI_PUBLIC struct logger_list *android_logger_list_alloc( - int mode, - unsigned int tail, - 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.tv_sec = 0; - logger_list->start.tv_nsec = 0; - logger_list->tail = tail; - logger_list->pid = pid; - logger_list->sock = -1; - - return logger_list; -} - -LIBLOG_ABI_PUBLIC 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; -} - -/* android_logger_list_register unimplemented, no use case */ -/* android_logger_list_unregister unimplemented, no use case */ - -/* Open the named log and add it to the logger list */ -LIBLOG_ABI_PUBLIC struct logger *android_logger_open( - struct logger_list *logger_list, - log_id_t id) -{ - struct logger *logger; - - if (!logger_list || (id >= LOG_ID_MAX)) { - goto err; - } - - logger_for_each(logger, logger_list) { - if (logger->id == id) { - goto ok; - } - } - - logger = calloc(1, sizeof(*logger)); - if (!logger) { - goto err; - } - - logger->id = id; - list_add_tail(&logger_list->node, &logger->node); - logger->top = logger_list; - goto ok; - -err: - logger = NULL; -ok: - return logger; -} - -/* Open the single named log and make it part of a new logger list */ -LIBLOG_ABI_PUBLIC struct logger_list *android_logger_list_open( - log_id_t id, - int mode, - unsigned int tail, - pid_t pid) -{ - struct logger_list *logger_list = android_logger_list_alloc(mode, tail, pid); - if (!logger_list) { - return NULL; - } - - if (!android_logger_open(logger_list, id)) { - android_logger_list_free(logger_list); - return NULL; - } - - return logger_list; -} - -static int android_logger_list_read_pstore(struct logger_list *logger_list, - struct log_msg *log_msg) -{ - ssize_t ret; - off_t current, next; - uid_t uid; - struct logger *logger; - struct __attribute__((__packed__)) { - android_pmsg_log_header_t p; - android_log_header_t l; - } buf; - static uint8_t preread_count; - bool is_system; - - memset(log_msg, 0, sizeof(*log_msg)); - - if (logger_list->sock < 0) { - int fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY); - - if (fd < 0) { - return -errno; - } - logger_list->sock = fd; - preread_count = 0; - } - - while(1) { - if (preread_count < sizeof(buf)) { - ret = TEMP_FAILURE_RETRY(read(logger_list->sock, - &buf.p.magic + preread_count, - sizeof(buf) - preread_count)); - if (ret < 0) { - return -errno; - } - preread_count += ret; - } - if (preread_count != sizeof(buf)) { - return preread_count ? -EIO : -EAGAIN; - } - if ((buf.p.magic != LOGGER_MAGIC) - || (buf.p.len <= sizeof(buf)) - || (buf.p.len > (sizeof(buf) + LOGGER_ENTRY_MAX_PAYLOAD)) - || (buf.l.id >= LOG_ID_MAX) - || (buf.l.realtime.tv_nsec >= NS_PER_SEC)) { - do { - memmove(&buf.p.magic, &buf.p.magic + 1, --preread_count); - } while (preread_count && (buf.p.magic != LOGGER_MAGIC)); - continue; - } - preread_count = 0; - - logger_for_each(logger, logger_list) { - if (buf.l.id != logger->id) { - continue; - } - - if ((logger_list->start.tv_sec || logger_list->start.tv_nsec) - && ((logger_list->start.tv_sec > buf.l.realtime.tv_sec) - || ((logger_list->start.tv_sec == buf.l.realtime.tv_sec) - && (logger_list->start.tv_nsec > buf.l.realtime.tv_nsec)))) { - break; - } - - if (logger_list->pid && (logger_list->pid != buf.p.pid)) { - break; - } - - uid = get_best_effective_uid(); - is_system = uid_has_log_permission(uid); - if (!is_system && (uid != buf.p.uid)) { - break; - } - - ret = TEMP_FAILURE_RETRY(read(logger_list->sock, - is_system ? - log_msg->entry_v4.msg : - log_msg->entry_v3.msg, - buf.p.len - sizeof(buf))); - if (ret < 0) { - return -errno; - } - if (ret != (ssize_t)(buf.p.len - sizeof(buf))) { - return -EIO; - } - - log_msg->entry_v4.len = buf.p.len - sizeof(buf); - log_msg->entry_v4.hdr_size = is_system ? - sizeof(log_msg->entry_v4) : - sizeof(log_msg->entry_v3); - log_msg->entry_v4.pid = buf.p.pid; - log_msg->entry_v4.tid = buf.l.tid; - log_msg->entry_v4.sec = buf.l.realtime.tv_sec; - log_msg->entry_v4.nsec = buf.l.realtime.tv_nsec; - log_msg->entry_v4.lid = buf.l.id; - if (is_system) { - log_msg->entry_v4.uid = buf.p.uid; - } - - return ret; - } - - current = TEMP_FAILURE_RETRY(lseek(logger_list->sock, - (off_t)0, SEEK_CUR)); - if (current < 0) { - return -errno; - } - next = TEMP_FAILURE_RETRY(lseek(logger_list->sock, - (off_t)(buf.p.len - sizeof(buf)), - SEEK_CUR)); - if (next < 0) { - return -errno; - } - if ((next - current) != (ssize_t)(buf.p.len - sizeof(buf))) { - return -EIO; - } - } -} - -static void caught_signal(int signum __unused) -{ -} - -/* Read from the selected logs */ -LIBLOG_ABI_PUBLIC int android_logger_list_read( - struct logger_list *logger_list, - struct log_msg *log_msg) -{ - int ret, e; - struct logger *logger; - struct sigaction ignore; - struct sigaction old_sigaction; - unsigned int old_alarm = 0; - - if (!logger_list) { - return -EINVAL; - } - - if (logger_list->mode & ANDROID_LOG_PSTORE) { - return android_logger_list_read_pstore(logger_list, log_msg); - } - - if (logger_list->mode & ANDROID_LOG_NONBLOCK) { - memset(&ignore, 0, sizeof(ignore)); - ignore.sa_handler = caught_signal; - sigemptyset(&ignore.sa_mask); - } - - if (logger_list->sock < 0) { - char buffer[256], *cp, c; - - int sock = socket_local_client("logdr", - ANDROID_SOCKET_NAMESPACE_RESERVED, - SOCK_SEQPACKET); - if (sock < 0) { - if ((sock == -1) && errno) { - return -errno; - } - return sock; - } - - strcpy(buffer, - (logger_list->mode & ANDROID_LOG_NONBLOCK) ? "dumpAndClose" : "stream"); - cp = buffer + strlen(buffer); - - 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 = ','; - } - - if (logger_list->tail) { - ret = snprintf(cp, remaining, " tail=%u", logger_list->tail); - ret = min(ret, remaining); - remaining -= ret; - cp += ret; - } - - if (logger_list->start.tv_sec || logger_list->start.tv_nsec) { - if (logger_list->mode & ANDROID_LOG_WRAP) { - // ToDo: alternate API to allow timeout to be adjusted. - ret = snprintf(cp, remaining, " timeout=%u", - ANDROID_LOG_WRAP_DEFAULT_TIMEOUT); - ret = min(ret, remaining); - remaining -= ret; - cp += ret; - } - 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; - } - - if (logger_list->pid) { - ret = snprintf(cp, remaining, " pid=%u", logger_list->pid); - ret = min(ret, remaining); - cp += ret; - } - - if (logger_list->mode & ANDROID_LOG_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 & ANDROID_LOG_NONBLOCK) { - if (e == EINTR) { - e = ETIMEDOUT; - } - alarm(old_alarm); - sigaction(SIGALRM, &old_sigaction, NULL); - } - - if (ret <= 0) { - close(sock); - if ((ret == -1) && e) { - return -e; - } - if (ret == 0) { - return -EIO; - } - return ret; - } - - logger_list->sock = sock; - } - - while(1) { - memset(log_msg, 0, sizeof(*log_msg)); - - if (logger_list->mode & ANDROID_LOG_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 & ANDROID_LOG_NONBLOCK) { - if ((ret == 0) || (e == EINTR)) { - e = EAGAIN; - ret = -1; - } - alarm(old_alarm); - sigaction(SIGALRM, &old_sigaction, NULL); - } - - if ((ret == -1) && e) { - return -e; - } - return ret; - } - /* NOTREACH */ - return ret; -} - -/* Close all the logs */ -LIBLOG_ABI_PUBLIC void android_logger_list_free( - struct logger_list *logger_list) -{ - if (logger_list == NULL) { - return; - } - - while (!list_empty(&logger_list->node)) { - struct listnode *node = list_head(&logger_list->node); - struct logger *logger = node_to_item(node, struct logger, node); - android_logger_free(logger); - } - - if (logger_list->sock >= 0) { - close (logger_list->sock); - } - - free(logger_list); -} diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp index b6af2221c..d2bf18166 100644 --- a/liblog/log_time.cpp +++ b/liblog/log_time.cpp @@ -21,7 +21,7 @@ #include -#include "log_cdefs.h" +#include "log_portability.h" LIBLOG_ABI_PRIVATE const char log_time::default_format[] = "%m-%d %H:%M:%S.%q"; LIBLOG_ABI_PRIVATE const timespec log_time::EPOCH = { 0, 0 }; diff --git a/liblog/logd_reader.c b/liblog/logd_reader.c new file mode 100644 index 000000000..d8441040c --- /dev/null +++ b/liblog/logd_reader.c @@ -0,0 +1,670 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "config_read.h" +#include "log_portability.h" +#include "logger.h" + +/* branchless on many architectures. */ +#define min(x,y) ((y) ^ (((x) ^ (y)) & -((x) < (y)))) + +static int logdAvailable(log_id_t LogId); +static int logdVersion(struct android_log_logger *logger, + struct android_log_transport_context *transp); +static int logdRead(struct android_log_logger_list *logger_list, + struct android_log_transport_context *transp, + struct log_msg *log_msg); +static int logdPoll(struct android_log_logger_list *logger_list, + struct android_log_transport_context *transp); +static void logdClose(struct android_log_logger_list *logger_list, + struct android_log_transport_context *transp); +static int logdClear(struct android_log_logger *logger, + struct android_log_transport_context *transp); +static ssize_t logdSetSize(struct android_log_logger *logger, + struct android_log_transport_context *transp, + size_t size); +static ssize_t logdGetSize(struct android_log_logger *logger, + struct android_log_transport_context *transp); +static ssize_t logdGetReadableSize(struct android_log_logger *logger, + struct android_log_transport_context *transp); +static ssize_t logdGetPrune(struct android_log_logger_list *logger, + struct android_log_transport_context *transp, + char *buf, size_t len); +static ssize_t logdSetPrune(struct android_log_logger_list *logger, + struct android_log_transport_context *transp, + char *buf, size_t len); +static ssize_t logdGetStats(struct android_log_logger_list *logger, + struct android_log_transport_context *transp, + char *buf, size_t len); + +LIBLOG_HIDDEN struct android_log_transport_read logdLoggerRead = { + .node = { &logdLoggerRead.node, &logdLoggerRead.node }, + .name = "logd", + .available = logdAvailable, + .version = logdVersion, + .read = logdRead, + .poll = logdPoll, + .close = logdClose, + .clear = logdClear, + .getSize = logdGetSize, + .setSize = logdSetSize, + .getReadableSize = logdGetSize, + .getPrune = logdGetPrune, + .setPrune = logdSetPrune, + .getStats = logdGetStats, +}; + +static int logdAvailable(log_id_t logId) +{ + if (logId > LOG_ID_KERNEL) { + return -EINVAL; + } + if (logId == LOG_ID_SECURITY) { + uid_t uid = __android_log_uid(); + if (uid != AID_SYSTEM) { + return -EPERM; + } + } + if (access("/dev/socket/logdw", W_OK) == 0) { + return 0; + } + return -EBADF; +} + +/* Private copy of ../libcutils/socket_local_client.c prevent library loops */ + +#if defined(_WIN32) + +LIBLOG_WEAK int socket_local_client(const char *name, int namespaceId, int type) +{ + errno = ENOSYS; + return -ENOSYS; +} + +#else /* !_WIN32 */ + +#include +#include +#include +#include + +/* 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. */ +LIBLOG_WEAK int 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: +#if defined(__linux__) + 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 + /* 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 + 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 + */ +LIBLOG_WEAK int 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; + +error: + return -1; +} + +/** + * connect to peer named "name" + * returns fd or -1 on error + */ +LIBLOG_WEAK int socket_local_client(const char *name, int namespaceId, int type) +{ + int s; + + s = socket(AF_LOCAL, type, 0); + if(s < 0) return -1; + + if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) { + close(s); + return -1; + } + + return s; +} + +#endif /* !_WIN32 */ +/* End of ../libcutils/socket_local_client.c */ + +/* worker for sending the command to the logger */ +static ssize_t send_log_msg(struct android_log_logger *logger, + const char *msg, char *buf, size_t buf_size) +{ + 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 (msg) { + snprintf(buf, buf_size, msg, logger ? logger->logId : (unsigned) -1); + } + + len = strlen(buf) + 1; + ret = TEMP_FAILURE_RETRY(write(sock, buf, len)); + if (ret <= 0) { + goto done; + } + + 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; + } + } + + 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; +} + +static int logdClear(struct android_log_logger *logger, + struct android_log_transport_context *transp __unused) +{ + 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 */ +static ssize_t logdGetSize(struct android_log_logger *logger, + struct android_log_transport_context *transp __unused) +{ + 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); +} + +static ssize_t logdSetSize( + struct android_log_logger *logger, + struct android_log_transport_context *transp __unused, + size_t size) +{ + char buf[512]; + + snprintf(buf, sizeof(buf), "setLogSize %d %zu", logger->logId, 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) + */ +static ssize_t logdGetReadableSize( + struct android_log_logger *logger, + struct android_log_transport_context *transp __unused) +{ + 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 + */ +static int logdVersion( + struct android_log_logger *logger __unused, + struct android_log_transport_context *transp __unused) +{ + uid_t uid = __android_log_uid(); + return ((uid != AID_ROOT) && (uid != AID_LOG) && (uid != AID_SYSTEM)) ? 3 : 4; +} + +/* + * returns statistics + */ +static ssize_t logdGetStats(struct android_log_logger_list *logger_list, + struct android_log_transport_context *transp __unused, + char *buf, size_t len) +{ + struct android_log_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->logId); + n = min(n, remaining); + remaining -= n; + cp += n; + } + + if (logger_list->pid) { + snprintf(cp, remaining, " pid=%u", logger_list->pid); + } + + return send_log_msg(NULL, NULL, buf, len); +} + +static ssize_t logdGetPrune( + struct android_log_logger_list *logger_list __unused, + struct android_log_transport_context *transp __unused, + char *buf, size_t len) +{ + return send_log_msg(NULL, "getPruneList", buf, len); +} + +static ssize_t logdSetPrune( + struct android_log_logger_list *logger_list __unused, + struct android_log_transport_context *transp __unused, + char *buf, size_t len) +{ + 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)); +} + + +static void caught_signal(int signum __unused) +{ +} + +static int logdOpen(struct android_log_logger_list *logger_list, + struct android_log_transport_context *transp) +{ + struct android_log_logger *logger; + struct sigaction ignore; + struct sigaction old_sigaction; + unsigned int old_alarm = 0; + char buffer[256], *cp, c; + int e, ret, remaining; + + int sock = transp->context.sock; + if (sock > 0) { + return sock; + } + + if (!logger_list) { + return -EINVAL; + } + + sock = socket_local_client("logdr", + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_SEQPACKET); + if (sock == 0) { + /* Guarantee not file descriptor zero */ + int newsock = socket_local_client("logdr", + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_SEQPACKET); + close(sock); + sock = newsock; + } + if (sock <= 0) { + if ((sock == -1) && errno) { + return -errno; + } + return sock; + } + + strcpy(buffer, (logger_list->mode & ANDROID_LOG_NONBLOCK) ? + "dumpAndClose" : "stream"); + cp = buffer + strlen(buffer); + + strcpy(cp, " lids"); + cp += 5; + c = '='; + remaining = sizeof(buffer) - (cp - buffer); + logger_for_each(logger, logger_list) { + ret = snprintf(cp, remaining, "%c%u", c, logger->logId); + ret = min(ret, remaining); + remaining -= ret; + cp += ret; + c = ','; + } + + if (logger_list->tail) { + ret = snprintf(cp, remaining, " tail=%u", logger_list->tail); + ret = min(ret, remaining); + remaining -= ret; + cp += ret; + } + + if (logger_list->start.tv_sec || logger_list->start.tv_nsec) { + if (logger_list->mode & ANDROID_LOG_WRAP) { + // ToDo: alternate API to allow timeout to be adjusted. + ret = snprintf(cp, remaining, " timeout=%u", + ANDROID_LOG_WRAP_DEFAULT_TIMEOUT); + ret = min(ret, remaining); + remaining -= ret; + cp += ret; + } + 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; + } + + if (logger_list->pid) { + ret = snprintf(cp, remaining, " pid=%u", logger_list->pid); + ret = min(ret, remaining); + cp += ret; + } + + if (logger_list->mode & ANDROID_LOG_NONBLOCK) { + /* Deal with an unresponsive logd */ + memset(&ignore, 0, sizeof(ignore)); + ignore.sa_handler = caught_signal; + sigemptyset(&ignore.sa_mask); + /* particularily useful if tombstone is reporting for logd */ + sigaction(SIGALRM, &ignore, &old_sigaction); + old_alarm = alarm(30); + } + ret = write(sock, buffer, cp - buffer); + e = errno; + if (logger_list->mode & ANDROID_LOG_NONBLOCK) { + if (e == EINTR) { + e = ETIMEDOUT; + } + alarm(old_alarm); + sigaction(SIGALRM, &old_sigaction, NULL); + } + + if (ret <= 0) { + close(sock); + if ((ret == -1) && e) { + return -e; + } + if (ret == 0) { + return -EIO; + } + return ret; + } + + return transp->context.sock = sock; +} + +/* Read from the selected logs */ +static int logdRead(struct android_log_logger_list *logger_list, + struct android_log_transport_context *transp, + struct log_msg *log_msg) +{ + int ret, e; + struct sigaction ignore; + struct sigaction old_sigaction; + unsigned int old_alarm = 0; + + ret = logdOpen(logger_list, transp); + if (ret < 0) { + return ret; + } + + memset(log_msg, 0, sizeof(*log_msg)); + + if (logger_list->mode & ANDROID_LOG_NONBLOCK) { + memset(&ignore, 0, sizeof(ignore)); + ignore.sa_handler = caught_signal; + sigemptyset(&ignore.sa_mask); + /* 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(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0); + e = errno; + + if (logger_list->mode & ANDROID_LOG_NONBLOCK) { + if ((ret == 0) || (e == EINTR)) { + e = EAGAIN; + ret = -1; + } + alarm(old_alarm); + sigaction(SIGALRM, &old_sigaction, NULL); + } + + if ((ret == -1) && e) { + return -e; + } + return ret; +} + +static int logdPoll(struct android_log_logger_list *logger_list, + struct android_log_transport_context *transp) +{ + struct pollfd p; + + int ret = logdOpen(logger_list, transp); + if (ret < 0) { + return ret; + } + + memset(&p, 0, sizeof(p)); + p.fd = ret; + p.events = POLLIN; + ret = poll(&p, 1, 20); + if ((ret > 0) && !(p.revents & POLLIN)) { + ret = 0; + } + if ((ret == -1) && errno) { + return -errno; + } + return ret; +} + +/* Close all the logs */ +static void logdClose(struct android_log_logger_list *logger_list __unused, + struct android_log_transport_context *transp) +{ + if (transp->context.sock > 0) { + close (transp->context.sock); + transp->context.sock = -1; + } +} diff --git a/liblog/logd_writer.c b/liblog/logd_writer.c new file mode 100644 index 000000000..696237d34 --- /dev/null +++ b/liblog/logd_writer.c @@ -0,0 +1,267 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "config_write.h" +#include "log_portability.h" +#include "logger.h" + +/* branchless on many architectures. */ +#define min(x,y) ((y) ^ (((x) ^ (y)) & -((x) < (y)))) + +static int logdAvailable(log_id_t LogId); +static int logdOpen(); +static void logdClose(); +static int logdWrite(log_id_t logId, struct timespec *ts, + struct iovec *vec, size_t nr); + +LIBLOG_HIDDEN struct android_log_transport_write logdLoggerWrite = { + .node = { &logdLoggerWrite.node, &logdLoggerWrite.node }, + .context.sock = -1, + .name = "logd", + .available = logdAvailable, + .open = logdOpen, + .close = logdClose, + .write = logdWrite, +}; + +/* log_init_lock assumed */ +static int logdOpen() +{ + int i, ret = 0; + + if (logdLoggerWrite.context.sock < 0) { + i = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)); + if (i < 0) { + ret = -errno; + } else if (TEMP_FAILURE_RETRY(fcntl(i, F_SETFL, O_NONBLOCK)) < 0) { + ret = -errno; + close(i); + } else { + struct sockaddr_un un; + memset(&un, 0, sizeof(struct sockaddr_un)); + un.sun_family = AF_UNIX; + strcpy(un.sun_path, "/dev/socket/logdw"); + + if (TEMP_FAILURE_RETRY(connect(i, (struct sockaddr *)&un, + sizeof(struct sockaddr_un))) < 0) { + ret = -errno; + close(i); + } else { + logdLoggerWrite.context.sock = i; + } + } + } + + return ret; +} + +static void logdClose() +{ + if (logdLoggerWrite.context.sock >= 0) { + close(logdLoggerWrite.context.sock); + logdLoggerWrite.context.sock = -1; + } +} + +static int logdAvailable(log_id_t logId) +{ + if (logId > LOG_ID_SECURITY) { + return -EINVAL; + } + if (logId == LOG_ID_SECURITY) { + uid_t uid = __android_log_uid(); + if ((uid != AID_LOG) && (uid != AID_ROOT) && (uid != AID_SYSTEM)) { + return -EPERM; + } + } + if (logdLoggerWrite.context.sock < 0) { + if (access("/dev/socket/logdw", W_OK) == 0) { + return 0; + } + return -EBADF; + } + return 1; +} + +static int logdWrite(log_id_t logId, struct timespec *ts, + struct iovec *vec, size_t nr) +{ + ssize_t ret; + static const unsigned headerLength = 1; + struct iovec newVec[nr + headerLength]; + android_log_header_t header; + size_t i, payloadSize; + static atomic_int_fast32_t dropped; + static atomic_int_fast32_t droppedSecurity; + + if (logdLoggerWrite.context.sock < 0) { + return -EBADF; + } + + /* logd, after initialization and priv drop */ + if (__android_log_uid() == AID_LOGD) { + /* + * ignore log messages we send to ourself (logd). + * Such log messages are often generated by libraries we depend on + * which use standard Android logging. + */ + return 0; + } + + /* + * struct { + * // what we provide to socket + * android_log_header_t header; + * // caller provides + * union { + * struct { + * char prio; + * char payload[]; + * } string; + * struct { + * uint32_t tag + * char payload[]; + * } binary; + * }; + * }; + */ + + header.tid = gettid(); + header.realtime.tv_sec = ts->tv_sec; + header.realtime.tv_nsec = ts->tv_nsec; + + newVec[0].iov_base = (unsigned char *)&header; + newVec[0].iov_len = sizeof(header); + + if (logdLoggerWrite.context.sock > 0) { + int32_t snapshot = atomic_exchange_explicit(&droppedSecurity, 0, + memory_order_relaxed); + if (snapshot) { + android_log_event_int_t buffer; + + header.id = LOG_ID_SECURITY; + buffer.header.tag = htole32(LIBLOG_LOG_TAG); + buffer.payload.type = EVENT_TYPE_INT; + buffer.payload.data = htole32(snapshot); + + newVec[headerLength].iov_base = &buffer; + newVec[headerLength].iov_len = sizeof(buffer); + + ret = TEMP_FAILURE_RETRY(writev(logdLoggerWrite.context.sock, newVec, 2)); + if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) { + atomic_fetch_add_explicit(&droppedSecurity, snapshot, + memory_order_relaxed); + } + } + snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed); + if (snapshot && __android_log_is_loggable(ANDROID_LOG_INFO, + "liblog", + ANDROID_LOG_VERBOSE)) { + android_log_event_int_t buffer; + + header.id = LOG_ID_EVENTS; + buffer.header.tag = htole32(LIBLOG_LOG_TAG); + buffer.payload.type = EVENT_TYPE_INT; + buffer.payload.data = htole32(snapshot); + + newVec[headerLength].iov_base = &buffer; + newVec[headerLength].iov_len = sizeof(buffer); + + ret = TEMP_FAILURE_RETRY(writev(logdLoggerWrite.context.sock, newVec, 2)); + if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) { + atomic_fetch_add_explicit(&dropped, snapshot, + memory_order_relaxed); + } + } + } + + header.id = logId; + + 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; + } + break; + } + } + + /* + * The write below could be lost, but will never block. + * + * ENOTCONN occurs if logd dies. + * EAGAIN occurs if logd is overloaded. + */ + ret = TEMP_FAILURE_RETRY(writev(logdLoggerWrite.context.sock, newVec, i)); + if (ret < 0) { + ret = -errno; + if (ret == -ENOTCONN) { + __android_log_lock(); + logdClose(); + ret = logdOpen(); + __android_log_unlock(); + + if (ret < 0) { + return ret; + } + + ret = TEMP_FAILURE_RETRY(writev(logdLoggerWrite.context.sock, newVec, i)); + if (ret < 0) { + ret = -errno; + } + } + } + + if (ret > (ssize_t)sizeof(header)) { + ret -= sizeof(header); + } else if (ret == -EAGAIN) { + atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed); + if (logId == LOG_ID_SECURITY) { + atomic_fetch_add_explicit(&droppedSecurity, 1, + memory_order_relaxed); + } + } + + return ret; +} diff --git a/liblog/logger.h b/liblog/logger.h new file mode 100644 index 000000000..61bc3968a --- /dev/null +++ b/liblog/logger.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 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. + */ + +#ifndef _LIBLOG_LOGGER_H__ +#define _LIBLOG_LOGGER_H__ + +#include +#include + +#include +#include +#include +#include + +#include "log_portability.h" + +__BEGIN_DECLS + +/* Union, sock or fd of zero is not allowed unless static initialized */ +union android_log_context { + void *private; + int sock; + int fd; + struct listnode *node; +}; + +struct android_log_transport_write { + struct listnode node; + const char *name; + union android_log_context context; /* Initialized by static allocation */ + + int (*available)(log_id_t logId); + int (*open)(); + void (*close)(); + int (*write)(log_id_t logId, struct timespec *ts, struct iovec *vec, size_t nr); +}; + +struct android_log_logger_list; +struct android_log_transport_context; +struct android_log_logger; + +struct android_log_transport_read { + struct listnode node; + const char *name; + + int (*available)(log_id_t logId); + int (*version)(struct android_log_logger *logger, + struct android_log_transport_context *transp); + void (*close)(struct android_log_logger_list *logger_list, + struct android_log_transport_context *transp); + + /* + * Expect all to instantiate open on any call, so we do not have + * an expicit open call + */ + int (*read)(struct android_log_logger_list *logger_list, + struct android_log_transport_context *transp, + struct log_msg *log_msg); + /* Assumption is only called if not ANDROID_LOG_NONBLOCK */ + int (*poll)(struct android_log_logger_list *logger_list, + struct android_log_transport_context *transp); + + int (*clear)(struct android_log_logger *logger, + struct android_log_transport_context *transp); + ssize_t (*setSize)(struct android_log_logger *logger, + struct android_log_transport_context *transp, + size_t size); + ssize_t (*getSize)(struct android_log_logger *logger, + struct android_log_transport_context *transp); + ssize_t (*getReadableSize)(struct android_log_logger *logger, + struct android_log_transport_context *transp); + + ssize_t (*getPrune)(struct android_log_logger_list *logger_list, + struct android_log_transport_context *transp, + char *buf, size_t len); + ssize_t (*setPrune)(struct android_log_logger_list *logger_list, + struct android_log_transport_context *transp, + char *buf, size_t len); + ssize_t (*getStats)(struct android_log_logger_list *logger_list, + struct android_log_transport_context *transp, + char *buf, size_t len); +}; + +struct android_log_logger_list { + struct listnode logger; + struct listnode transport; + int mode; + unsigned int tail; + log_time start; + pid_t pid; +}; + +struct android_log_logger { + struct listnode node; + struct android_log_logger_list *parent; + + log_id_t logId; +}; + +struct android_log_transport_context { + struct listnode node; + union android_log_context context; /* zero init per-transport context */ + struct android_log_logger_list *parent; + + struct android_log_transport_read *transport; + unsigned logMask; + int ret; + struct log_msg logMsg; /* valid is logMsg.len != 0 */ +}; + +/* assumes caller has structures read-locked, single threaded, or fenced */ +#define transport_context_for_each(transp, logger_list) \ + for (transp = node_to_item((logger_list)->transport.next, \ + struct android_log_transport_context, \ + node); \ + (transp != node_to_item(&(logger_list)->transport, \ + struct android_log_transport_context, \ + node)) && \ + (transp->parent == (logger_list)); \ + transp = node_to_item(transp->node.next, \ + struct android_log_transport_context, node)) + +#define logger_for_each(logp, logger_list) \ + for (logp = node_to_item((logger_list)->logger.next, \ + struct android_log_logger, node); \ + (logp != node_to_item(&(logger_list)->logger, \ + struct android_log_logger, node)) && \ + (logp->parent == (logger_list)); \ + logp = node_to_item((logp)->node.next, \ + struct android_log_logger, node)) + +/* OS specific dribs and drabs */ + +#if defined(_WIN32) +typedef uint32_t uid_t; +#endif + +LIBLOG_HIDDEN uid_t __android_log_uid(); +LIBLOG_HIDDEN pid_t __android_log_pid(); +LIBLOG_HIDDEN void __android_log_lock(); +LIBLOG_HIDDEN int __android_log_trylock(); +LIBLOG_HIDDEN void __android_log_unlock(); + +__END_DECLS + +#endif /* _LIBLOG_LOGGER_H__ */ diff --git a/liblog/logger_lock.c b/liblog/logger_lock.c new file mode 100644 index 000000000..ee979bd5e --- /dev/null +++ b/liblog/logger_lock.c @@ -0,0 +1,82 @@ +/* + * 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. + */ + +/* + * Some OS specific dribs and drabs (locking etc). + */ + +#if !defined(_WIN32) +#include +#endif + +#include + +#include "logger.h" + +LIBLOG_HIDDEN uid_t __android_log_uid() +{ +#if defined(_WIN32) + return AID_SYSTEM; +#else + static uid_t last_uid = AID_ROOT; /* logd *always* starts up as AID_ROOT */ + + if (last_uid == AID_ROOT) { /* have we called to get the UID yet? */ + last_uid = getuid(); + } + return last_uid; +#endif +} + +LIBLOG_HIDDEN pid_t __android_log_pid() +{ + static pid_t last_pid = (pid_t) -1; + + if (last_pid == (pid_t) -1) { + last_pid = getpid(); + } + return last_pid; +} + +#if !defined(_WIN32) +static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; +#endif + +LIBLOG_HIDDEN void __android_log_lock() +{ +#if !defined(_WIN32) + /* + * If we trigger a signal handler in the middle of locked activity and the + * signal handler logs a message, we could get into a deadlock state. + */ + pthread_mutex_lock(&log_init_lock); +#endif +} + +LIBLOG_HIDDEN int __android_log_trylock() +{ +#if !defined(_WIN32) + return pthread_mutex_trylock(&log_init_lock); +#else + return 0; +#endif +} + +LIBLOG_HIDDEN void __android_log_unlock() +{ +#if !defined(_WIN32) + pthread_mutex_unlock(&log_init_lock); +#endif +} diff --git a/liblog/logger_name.c b/liblog/logger_name.c new file mode 100644 index 000000000..b7ccac51f --- /dev/null +++ b/liblog/logger_name.c @@ -0,0 +1,65 @@ +/* +** Copyright 2013-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 + +#include +#include + +#include "log_portability.h" + +/* In the future, we would like to make this list extensible */ +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_CRASH] = "crash", + [LOG_ID_SECURITY] = "security", + [LOG_ID_KERNEL] = "kernel", +}; + +LIBLOG_ABI_PUBLIC 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]; +} + +LIBLOG_ABI_PUBLIC log_id_t android_name_to_log_id(const char *logName) +{ + const char *b; + int ret; + + if (!logName) { + return -1; /* NB: log_id_t is unsigned */ + } + b = strrchr(logName, '/'); + if (!b) { + b = logName; + } else { + ++b; + } + + for(ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) { + const char *l = LOG_NAME[ret]; + if (l && !strcmp(b, l)) { + return ret; + } + } + return -1; /* should never happen */ +} diff --git a/liblog/logger_read.c b/liblog/logger_read.c new file mode 100644 index 000000000..f15c7cd62 --- /dev/null +++ b/liblog/logger_read.c @@ -0,0 +1,474 @@ +/* +** Copyright 2013-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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "config_read.h" +#include "log_portability.h" +#include "logger.h" + +/* android_logger_alloc unimplemented, no use case */ +/* android_logger_free not exported */ +static void android_logger_free(struct logger *logger) +{ + struct android_log_logger *logger_internal = + (struct android_log_logger *)logger; + + if (!logger_internal) { + return; + } + + list_remove(&logger_internal->node); + + free(logger_internal); +} + +/* android_logger_alloc unimplemented, no use case */ + +/* method for getting the associated sublog id */ +LIBLOG_ABI_PUBLIC log_id_t android_logger_get_id(struct logger *logger) +{ + return ((struct android_log_logger *)logger)->logId; +} + +static int init_transport_context(struct android_log_logger_list *logger_list) +{ + struct android_log_transport_read *transport; + struct listnode *node; + + if (!logger_list) { + return -EINVAL; + } + + if (list_empty(&logger_list->logger)) { + return -EINVAL; + } + + if (!list_empty(&logger_list->transport)) { + return 0; + } + + __android_log_lock(); + /* mini __write_to_log_initialize() to populate transports */ + if (list_empty(&__android_log_transport_read) && + list_empty(&__android_log_persist_read)) { + __android_log_config_read(); + } + __android_log_unlock(); + + node = (logger_list->mode & ANDROID_LOG_PSTORE) ? + &__android_log_persist_read : &__android_log_transport_read; + + read_transport_for_each(transport, node) { + struct android_log_transport_context *transp; + struct android_log_logger *logger; + unsigned logMask = 0; + + logger_for_each(logger, logger_list) { + log_id_t logId = logger->logId; + + if (transport->read && + (!transport->available || + (transport->available(logId) >= 0))) { + logMask |= 1 << logId; + } + } + if (!logMask) { + continue; + } + transp = calloc(1, sizeof(*transp)); + if (!transp) { + return -ENOMEM; + } + transp->parent = logger_list; + transp->transport = transport; + transp->logMask = logMask; + transp->ret = 1; + list_add_tail(&logger_list->transport, &transp->node); + } + if (list_empty(&logger_list->transport)) { + return -ENODEV; + } + return 0; +} + +#define LOGGER_FUNCTION(logger, def, func, args...) \ + ssize_t ret = -EINVAL; \ + struct android_log_transport_context *transp; \ + struct android_log_logger *logger_internal = \ + (struct android_log_logger *)logger; \ + \ + if (!logger_internal) { \ + return ret; \ + } \ + ret = init_transport_context(logger_internal->parent); \ + if (ret < 0) { \ + return ret; \ + } \ + \ + ret = (def); \ + transport_context_for_each(transp, logger_internal->parent) { \ + if ((transp->logMask & (1 << logger_internal->logId)) && \ + transp->transport && transp->transport->func) { \ + ssize_t retval = (transp->transport->func)(logger_internal, \ + transp, ## args); \ + if ((ret >= 0) || (ret == (def))) { \ + ret = retval; \ + } \ + } \ + } \ + return ret + +LIBLOG_ABI_PUBLIC int android_logger_clear(struct logger *logger) +{ + LOGGER_FUNCTION(logger, -ENODEV, clear); +} + +/* returns the total size of the log's ring buffer */ +LIBLOG_ABI_PUBLIC long android_logger_get_log_size(struct logger *logger) +{ + LOGGER_FUNCTION(logger, -ENODEV, getSize); +} + +LIBLOG_ABI_PUBLIC int android_logger_set_log_size(struct logger *logger, + unsigned long size) +{ + LOGGER_FUNCTION(logger, -ENODEV, setSize, size); +} + +/* + * returns the readable size of the log's ring buffer (that is, amount of the + * log consumed) + */ +LIBLOG_ABI_PUBLIC long android_logger_get_log_readable_size( + struct logger *logger) +{ + LOGGER_FUNCTION(logger, -ENODEV, getReadableSize); +} + +/* + * returns the logger version + */ +LIBLOG_ABI_PUBLIC int android_logger_get_log_version(struct logger *logger) +{ + LOGGER_FUNCTION(logger, 4, version); +} + +#define LOGGER_LIST_FUNCTION(logger_list, def, func, args...) \ + struct android_log_transport_context *transp; \ + struct android_log_logger_list *logger_list_internal = \ + (struct android_log_logger_list *)logger_list; \ + \ + ssize_t ret = init_transport_context(logger_list_internal); \ + if (ret < 0) { \ + return ret; \ + } \ + \ + ret = (def); \ + transport_context_for_each(transp, logger_list_internal) { \ + if (transp->transport && (transp->transport->func)) { \ + ssize_t retval = (transp->transport->func)(logger_list_internal, \ + transp, ## args); \ + if ((ret >= 0) || (ret == (def))) { \ + ret = retval; \ + } \ + } \ + } \ + return ret + +/* + * returns statistics + */ +LIBLOG_ABI_PUBLIC ssize_t android_logger_get_statistics( + struct logger_list *logger_list, + char *buf, size_t len) +{ + LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getStats, buf, len); +} + +LIBLOG_ABI_PUBLIC ssize_t android_logger_get_prune_list( + struct logger_list *logger_list, + char *buf, size_t len) +{ + LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getPrune, buf, len); +} + +LIBLOG_ABI_PUBLIC int android_logger_set_prune_list( + struct logger_list *logger_list, + char *buf, size_t len) +{ + LOGGER_LIST_FUNCTION(logger_list, -ENODEV, setPrune, buf, len); +} + +LIBLOG_ABI_PUBLIC struct logger_list *android_logger_list_alloc( + int mode, + unsigned int tail, + pid_t pid) +{ + struct android_log_logger_list *logger_list; + + logger_list = calloc(1, sizeof(*logger_list)); + if (!logger_list) { + return NULL; + } + + list_init(&logger_list->logger); + list_init(&logger_list->transport); + logger_list->mode = mode; + logger_list->tail = tail; + logger_list->pid = pid; + + return (struct logger_list *)logger_list; +} + +LIBLOG_ABI_PUBLIC struct logger_list *android_logger_list_alloc_time( + int mode, + log_time start, + pid_t pid) +{ + struct android_log_logger_list *logger_list; + + logger_list = calloc(1, sizeof(*logger_list)); + if (!logger_list) { + return NULL; + } + + list_init(&logger_list->logger); + list_init(&logger_list->transport); + logger_list->mode = mode; + logger_list->start = start; + logger_list->pid = pid; + + return (struct logger_list *)logger_list; +} + +/* android_logger_list_register unimplemented, no use case */ +/* android_logger_list_unregister unimplemented, no use case */ + +/* Open the named log and add it to the logger list */ +LIBLOG_ABI_PUBLIC struct logger *android_logger_open( + struct logger_list *logger_list, + log_id_t logId) +{ + struct android_log_logger_list *logger_list_internal = + (struct android_log_logger_list *)logger_list; + struct android_log_logger *logger; + + if (!logger_list_internal || (logId >= LOG_ID_MAX)) { + goto err; + } + + logger_for_each(logger, logger_list_internal) { + if (logger->logId == logId) { + goto ok; + } + } + + logger = calloc(1, sizeof(*logger)); + if (!logger) { + goto err; + } + + logger->logId = logId; + list_add_tail(&logger_list_internal->logger, &logger->node); + logger->parent = logger_list_internal; + + /* Reset known transports to re-evaluate, we just added one */ + while (!list_empty(&logger_list_internal->transport)) { + struct listnode *node = list_head(&logger_list_internal->transport); + struct android_log_transport_context *transp = + node_to_item(node, struct android_log_transport_context, node); + + list_remove(&transp->node); + free(transp); + } + goto ok; + +err: + logger = NULL; +ok: + return (struct logger *)logger; +} + +/* Open the single named log and make it part of a new logger list */ +LIBLOG_ABI_PUBLIC struct logger_list *android_logger_list_open( + log_id_t logId, + int mode, + unsigned int tail, + pid_t pid) +{ + struct logger_list *logger_list = + android_logger_list_alloc(mode, tail, pid); + + if (!logger_list) { + return NULL; + } + + if (!android_logger_open(logger_list, logId)) { + android_logger_list_free(logger_list); + return NULL; + } + + return logger_list; +} + +/* Read from the selected logs */ +LIBLOG_ABI_PUBLIC int android_logger_list_read(struct logger_list *logger_list, + struct log_msg *log_msg) +{ + struct android_log_transport_context *transp; + struct android_log_logger_list *logger_list_internal = + (struct android_log_logger_list *)logger_list; + + int ret = init_transport_context(logger_list_internal); + if (ret < 0) { + return ret; + } + + /* at least one transport */ + transp = node_to_item(logger_list_internal->transport.next, + struct android_log_transport_context, node); + + /* more than one transport? */ + if (transp->node.next != &logger_list_internal->transport) { + /* Poll and merge sort the entries if from multiple transports */ + struct android_log_transport_context *oldest = NULL; + int ret; + int polled = 0; + do { + if (polled) { + sched_yield(); + } + ret = -1000; + polled = 0; + do { + int retval = transp->ret; + if ((retval > 0) && !transp->logMsg.entry.len) { + if (!transp->transport->read) { + retval = transp->ret = 0; + } else if ((logger_list_internal->mode & + ANDROID_LOG_NONBLOCK) || + !transp->transport->poll) { + retval = transp->ret = (*transp->transport->read)( + logger_list_internal, + transp, + &transp->logMsg); + } else { + int pollval = (*transp->transport->poll)( + logger_list_internal, transp); + if (pollval <= 0) { + sched_yield(); + pollval = (*transp->transport->poll)( + logger_list_internal, transp); + } + polled = 1; + if (pollval < 0) { + if ((pollval == -EINTR) || (pollval == -EAGAIN)) { + return -EAGAIN; + } + retval = transp->ret = pollval; + } else if (pollval > 0) { + retval = transp->ret = (*transp->transport->read)( + logger_list_internal, + transp, + &transp->logMsg); + } + } + } + if (ret < retval) { + ret = retval; + } + if ((transp->ret > 0) && transp->logMsg.entry.len && + (!oldest || + (oldest->logMsg.entry.sec > + transp->logMsg.entry.sec) || + ((oldest->logMsg.entry.sec == + transp->logMsg.entry.sec) && + (oldest->logMsg.entry.nsec > + transp->logMsg.entry.nsec)))) { + oldest = transp; + } + transp = node_to_item(transp->node.next, + struct android_log_transport_context, + node); + } while (transp != node_to_item( + &logger_list_internal->transport, + struct android_log_transport_context, + node)); + if (!oldest && + (logger_list_internal->mode & ANDROID_LOG_NONBLOCK)) { + return (ret < 0) ? ret : -EAGAIN; + } + transp = node_to_item(logger_list_internal->transport.next, + struct android_log_transport_context, node); + } while (!oldest && (ret > 0)); + if (!oldest) { + return ret; + } + memcpy(log_msg, &oldest->logMsg, oldest->logMsg.entry.len + + (oldest->logMsg.entry.hdr_size ? + oldest->logMsg.entry.hdr_size : + sizeof(struct logger_entry))); + oldest->logMsg.entry.len = 0; /* Mark it as copied */ + return oldest->ret; + } + + /* if only one, no need to copy into transport_context and merge-sort */ + return (transp->transport->read)(logger_list_internal, transp, log_msg); +} + +/* Close all the logs */ +LIBLOG_ABI_PUBLIC void android_logger_list_free(struct logger_list *logger_list) +{ + struct android_log_logger_list *logger_list_internal = + (struct android_log_logger_list *)logger_list; + + if (logger_list_internal == NULL) { + return; + } + + while (!list_empty(&logger_list_internal->transport)) { + struct listnode *node = list_head(&logger_list_internal->transport); + struct android_log_transport_context *transp = + node_to_item(node, struct android_log_transport_context, node); + + if (transp->transport && transp->transport->close) { + (*transp->transport->close)(logger_list_internal, transp); + } + list_remove(&transp->node); + free(transp); + } + + while (!list_empty(&logger_list_internal->logger)) { + struct listnode *node = list_head(&logger_list_internal->logger); + struct android_log_logger *logger = + node_to_item(node, struct android_log_logger, node); + android_logger_free((struct logger *)logger); + } + + free(logger_list_internal); +} diff --git a/liblog/logd_write.c b/liblog/logger_write.c similarity index 54% rename from liblog/logd_write.c rename to liblog/logger_write.c index 85a4aab52..a4155e927 100644 --- a/liblog/logd_write.c +++ b/liblog/logger_write.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2014 The Android Open Source Project + * 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. @@ -13,27 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#if (FAKE_LOG_DEVICE == 0) -#include -#endif + #include -#include -#if !defined(_WIN32) -#include -#endif -#include #include -#include #include #include -#include -#include -#if (FAKE_LOG_DEVICE == 0) -#include -#include -#endif -#include -#include +#include #ifdef __BIONIC__ #include @@ -46,55 +31,15 @@ #include #include -#include "log_cdefs.h" +#include "config_write.h" +#include "log_portability.h" +#include "logger.h" #define LOG_BUF_SIZE 1024 -#if FAKE_LOG_DEVICE -/* This will be defined when building for the host. */ -#include "fake_log_device.h" -#endif - static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr); static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init; -#if !defined(_WIN32) -static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; - -static void lock() -{ - /* - * If we trigger a signal handler in the middle of locked activity and the - * signal handler logs a message, we could get into a deadlock state. - */ - pthread_mutex_lock(&log_init_lock); -} - -static int trylock() -{ - return pthread_mutex_trylock(&log_init_lock); -} - -static void unlock() -{ - pthread_mutex_unlock(&log_init_lock); -} - -#else /* !defined(_WIN32) */ - -#define lock() ((void)0) -#define trylock() (0) /* success */ -#define unlock() ((void)0) - -#endif /* !defined(_WIN32) */ - -#if FAKE_LOG_DEVICE -static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1, -1 }; -#else -static int logd_fd = -1; -static int pstore_fd = -1; -#endif - /* * This is used by the C++ code to decide if it should write logs through * the C code. Basically, if /dev/socket/logd is available, we're running in @@ -106,110 +51,101 @@ static enum { LIBLOG_ABI_PUBLIC int __android_log_dev_available() { - if (g_log_status == kLogUninitialized) { - if (access("/dev/socket/logdw", W_OK) == 0) - g_log_status = kLogAvailable; - else - g_log_status = kLogNotAvailable; - } + struct android_log_transport_write *node; + size_t i; - return (g_log_status == kLogAvailable); + if (list_empty(&__android_log_transport_write)) { + return kLogUninitialized; + } + for (i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) { + write_transport_for_each(node, &__android_log_transport_write) { + if (node->write && + (!node->available || ((*node->available)(i) >= 0))) { + return kLogAvailable; + } + } + } + return kLogNotAvailable; } /* log_init_lock assumed */ static int __write_to_log_initialize() { - int i, ret = 0; + struct android_log_transport_write *transport; + struct listnode *n; + int i = 0, ret = 0; -#if FAKE_LOG_DEVICE - for (i = 0; i < LOG_ID_MAX; i++) { - char buf[sizeof("/dev/log_security")]; - snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i)); - log_fds[i] = fakeLogOpen(buf, O_WRONLY); - } -#else - if (pstore_fd < 0) { - pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY)); - } - - if (logd_fd < 0) { - i = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)); - if (i < 0) { - ret = -errno; - } else if (TEMP_FAILURE_RETRY(fcntl(i, F_SETFL, O_NONBLOCK)) < 0) { - ret = -errno; - close(i); - } else { - struct sockaddr_un un; - memset(&un, 0, sizeof(struct sockaddr_un)); - un.sun_family = AF_UNIX; - strcpy(un.sun_path, "/dev/socket/logdw"); - - if (TEMP_FAILURE_RETRY(connect(i, (struct sockaddr *)&un, - sizeof(struct sockaddr_un))) < 0) { - ret = -errno; - close(i); - } else { - logd_fd = i; + __android_log_config_write(); + write_transport_for_each_safe(transport, n, &__android_log_transport_write) { + if (!transport->open || ((*transport->open)() < 0)) { + if (transport->close) { + (*transport->close)(); } + list_remove(&transport->node); + continue; } + ++ret; + } + write_transport_for_each_safe(transport, n, &__android_log_persist_write) { + if (!transport->open || ((*transport->open)() < 0)) { + if (transport->close) { + (*transport->close)(); + } + list_remove(&transport->node); + continue; + } + ++i; + } + if (!ret && !i) { + return -ENODEV; } -#endif return ret; } +/* + * Extract a 4-byte value from a byte stream. le32toh open coded + */ +static inline uint32_t get4LE(const uint8_t* src) +{ + return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); +} + static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr) { - ssize_t ret; -#if FAKE_LOG_DEVICE - int log_fd; - - if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) { - log_fd = log_fds[(int)log_id]; - } else { - return -EBADF; - } - do { - ret = fakeLogWritev(log_fd, vec, nr); - if (ret < 0) { - ret = -errno; - } - } while (ret == -EINTR); -#else - static const unsigned header_length = 2; - struct iovec newVec[nr + header_length]; - android_log_header_t header; - android_pmsg_log_header_t pmsg_header; + struct android_log_transport_write *node; + int ret; struct timespec ts; - size_t i, payload_size; - static uid_t last_uid = AID_ROOT; /* logd *always* starts up as AID_ROOT */ - static pid_t last_pid = (pid_t) -1; - static atomic_int_fast32_t dropped; - static atomic_int_fast32_t dropped_security; + size_t len, i; - if (!nr) { + for (len = i = 0; i < nr; ++i) { + len += vec[i].iov_len; + } + if (!len) { return -EINVAL; } - if (last_uid == AID_ROOT) { /* have we called to get the UID yet? */ - last_uid = getuid(); - } - if (last_pid == (pid_t) -1) { - last_pid = getpid(); - } +#if defined(__BIONIC__) if (log_id == LOG_ID_SECURITY) { + uid_t uid; + if (vec[0].iov_len < 4) { return -EINVAL; } + + uid = __android_log_uid(); /* Matches clientHasLogCredentials() in logd */ - if ((last_uid != AID_SYSTEM) && (last_uid != AID_ROOT) && (last_uid != AID_LOG)) { - uid_t uid = geteuid(); + if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) { + uid = geteuid(); if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) { gid_t gid = getgid(); - if ((gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) { + if ((gid != AID_SYSTEM) && + (gid != AID_ROOT) && + (gid != AID_LOG)) { gid = getegid(); - if ((gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) { + if ((gid != AID_SYSTEM) && + (gid != AID_ROOT) && + (gid != AID_LOG)) { int num_groups; gid_t *groups; @@ -237,12 +173,11 @@ static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr) } } if (!__android_log_security()) { - atomic_store(&dropped_security, 0); + /* If only we could reset downstream logd counter */ return -EPERM; } } else if (log_id == LOG_ID_EVENTS) { static atomic_uintptr_t map; - int ret; const char *tag; EventTagMap *m, *f; @@ -255,7 +190,7 @@ static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr) m = (EventTagMap *)atomic_load(&map); if (!m) { - ret = trylock(); + ret = __android_log_trylock(); m = (EventTagMap *)atomic_load(&map); /* trylock flush cache */ if (!m) { m = android_openEventTagMap(EVENT_TAG_MAP_FILE); @@ -269,13 +204,11 @@ static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr) } } if (!ret) { /* trylock succeeded, unlock */ - unlock(); + __android_log_unlock(); } } if (m && (m != (EventTagMap *)(uintptr_t)-1LL)) { - tag = android_lookupEventTag( - m, - htole32(((uint32_t *)vec[0].iov_base)[0])); + tag = android_lookupEventTag(m, get4LE(vec[0].iov_base)); } ret = __android_log_is_loggable(ANDROID_LOG_INFO, tag, @@ -317,203 +250,57 @@ static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr) } } - /* - * struct { - * // what we provide to pstore - * android_pmsg_log_header_t pmsg_header; - * // what we provide to socket - * android_log_header_t header; - * // caller provides - * union { - * struct { - * char prio; - * char payload[]; - * } string; - * struct { - * uint32_t tag - * char payload[]; - * } binary; - * }; - * }; - */ - clock_gettime(android_log_clockid(), &ts); - - pmsg_header.magic = LOGGER_MAGIC; - pmsg_header.len = sizeof(pmsg_header) + sizeof(header); - pmsg_header.uid = last_uid; - pmsg_header.pid = last_pid; - - header.tid = gettid(); - header.realtime.tv_sec = ts.tv_sec; - header.realtime.tv_nsec = ts.tv_nsec; - - newVec[0].iov_base = (unsigned char *) &pmsg_header; - newVec[0].iov_len = sizeof(pmsg_header); - newVec[1].iov_base = (unsigned char *) &header; - newVec[1].iov_len = sizeof(header); - - if (logd_fd > 0) { - int32_t snapshot = atomic_exchange_explicit(&dropped_security, 0, - memory_order_relaxed); - if (snapshot) { - android_log_event_int_t buffer; - - header.id = LOG_ID_SECURITY; - buffer.header.tag = htole32(LIBLOG_LOG_TAG); - buffer.payload.type = EVENT_TYPE_INT; - buffer.payload.data = htole32(snapshot); - - newVec[2].iov_base = &buffer; - newVec[2].iov_len = sizeof(buffer); - - ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, 2)); - if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) { - atomic_fetch_add_explicit(&dropped_security, snapshot, - memory_order_relaxed); - } - } - snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed); - if (snapshot && __android_log_is_loggable(ANDROID_LOG_INFO, - "liblog", - ANDROID_LOG_VERBOSE)) { - android_log_event_int_t buffer; - - header.id = LOG_ID_EVENTS; - buffer.header.tag = htole32(LIBLOG_LOG_TAG); - buffer.payload.type = EVENT_TYPE_INT; - buffer.payload.data = htole32(snapshot); - - newVec[2].iov_base = &buffer; - newVec[2].iov_len = sizeof(buffer); - - ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, 2)); - if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) { - atomic_fetch_add_explicit(&dropped, snapshot, - memory_order_relaxed); - } - } - } - - header.id = log_id; - - for (payload_size = 0, i = header_length; i < nr + header_length; i++) { - newVec[i].iov_base = vec[i - header_length].iov_base; - payload_size += newVec[i].iov_len = vec[i - header_length].iov_len; - - if (payload_size > LOGGER_ENTRY_MAX_PAYLOAD) { - newVec[i].iov_len -= payload_size - LOGGER_ENTRY_MAX_PAYLOAD; - if (newVec[i].iov_len) { - ++i; - } - payload_size = LOGGER_ENTRY_MAX_PAYLOAD; - break; - } - } - pmsg_header.len += payload_size; - - if (pstore_fd >= 0) { - TEMP_FAILURE_RETRY(writev(pstore_fd, newVec, i)); - } - - if (last_uid == AID_LOGD) { /* logd, after initialization and priv drop */ - /* - * ignore log messages we send to ourself (logd). - * Such log messages are often generated by libraries we depend on - * which use standard Android logging. - */ - return 0; - } - - if (logd_fd < 0) { - return -EBADF; - } - - /* - * The write below could be lost, but will never block. - * - * To logd, we drop the pmsg_header - * - * ENOTCONN occurs if logd dies. - * EAGAIN occurs if logd is overloaded. - */ - ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, i - 1)); - if (ret < 0) { - ret = -errno; - if (ret == -ENOTCONN) { - lock(); - close(logd_fd); - logd_fd = -1; - ret = __write_to_log_initialize(); - unlock(); - - if (ret < 0) { - return ret; - } - - ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, i - 1)); - if (ret < 0) { - ret = -errno; - } - } - } - - if (ret > (ssize_t)sizeof(header)) { - ret -= sizeof(header); - } else if (ret == -EAGAIN) { - atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed); - if (log_id == LOG_ID_SECURITY) { - atomic_fetch_add_explicit(&dropped_security, 1, - memory_order_relaxed); - } +#else + /* simulate clock_gettime(CLOCK_REALTIME, &ts); */ + { + struct timeval tv; + gettimeofday(&tv, NULL); + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; } #endif + ret = 0; + write_transport_for_each(node, &__android_log_transport_write) { + if (node->write) { + ssize_t retval; + retval = (*node->write)(log_id, &ts, vec, nr); + if (ret >= 0) { + ret = retval; + } + } + } + + write_transport_for_each(node, &__android_log_persist_write) { + if (node->write) { + (void)(*node->write)(log_id, &ts, vec, nr); + } + } + return ret; } -#if FAKE_LOG_DEVICE -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_CRASH] = "crash", - [LOG_ID_SECURITY] = "security", - [LOG_ID_KERNEL] = "kernel", -}; - -LIBLOG_ABI_PUBLIC 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]; -} -#endif - static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) { - lock(); + __android_log_lock(); if (write_to_log == __write_to_log_init) { int ret; ret = __write_to_log_initialize(); if (ret < 0) { - unlock(); -#if (FAKE_LOG_DEVICE == 0) - if (pstore_fd >= 0) { + __android_log_unlock(); + if (!list_empty(&__android_log_persist_write)) { __write_to_log_daemon(log_id, vec, nr); } -#endif return ret; } write_to_log = __write_to_log_daemon; } - unlock(); + __android_log_unlock(); return write_to_log(log_id, vec, nr); } @@ -603,10 +390,8 @@ LIBLOG_ABI_PUBLIC int __android_log_buf_print(int bufID, int prio, return __android_log_buf_write(bufID, prio, tag, buf); } -LIBLOG_ABI_PUBLIC void __android_log_assert( - const char *cond, - const char *tag, - const char *fmt, ...) +LIBLOG_ABI_PUBLIC void __android_log_assert(const char *cond, const char *tag, + const char *fmt, ...) { char buf[LOG_BUF_SIZE]; diff --git a/liblog/logprint.c b/liblog/logprint.c index 02df8ddd6..d7de8648b 100644 --- a/liblog/logprint.c +++ b/liblog/logprint.c @@ -34,7 +34,7 @@ #include #include -#include "log_cdefs.h" +#include "log_portability.h" #define MS_PER_NSEC 1000000 #define US_PER_NSEC 1000 diff --git a/liblog/pmsg_reader.c b/liblog/pmsg_reader.c new file mode 100644 index 000000000..5695e8ae5 --- /dev/null +++ b/liblog/pmsg_reader.c @@ -0,0 +1,587 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "config_read.h" +#include "logger.h" + +static int pmsgAvailable(log_id_t logId); +static int pmsgVersion(struct android_log_logger *logger, + struct android_log_transport_context *transp); +static int pmsgRead(struct android_log_logger_list *logger_list, + struct android_log_transport_context *transp, + struct log_msg *log_msg); +static void pmsgClose(struct android_log_logger_list *logger_list, + struct android_log_transport_context *transp); +static int pmsgClear(struct android_log_logger *logger, + struct android_log_transport_context *transp); + +LIBLOG_HIDDEN struct android_log_transport_read pmsgLoggerRead = { + .node = { &pmsgLoggerRead.node, &pmsgLoggerRead.node }, + .name = "pmsg", + .available = pmsgAvailable, + .version = pmsgVersion, + .read = pmsgRead, + .poll = NULL, + .close = pmsgClose, + .clear = pmsgClear, + .setSize = NULL, + .getSize = NULL, + .getReadableSize = NULL, + .getPrune = NULL, + .setPrune = NULL, + .getStats = NULL, +}; + +static int pmsgAvailable(log_id_t logId) +{ + if (logId > LOG_ID_SECURITY) { + return -EINVAL; + } + if (access("/dev/pmsg0", W_OK) == 0) { + return 0; + } + return -EBADF; +} + +/* Determine the credentials of the caller */ +static bool uid_has_log_permission(uid_t uid) +{ + return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT); +} + +static uid_t get_best_effective_uid() +{ + uid_t euid; + uid_t uid; + gid_t gid; + ssize_t i; + static uid_t last_uid = (uid_t) -1; + + if (last_uid != (uid_t) -1) { + return last_uid; + } + uid = __android_log_uid(); + if (uid_has_log_permission(uid)) { + return last_uid = uid; + } + euid = geteuid(); + if (uid_has_log_permission(euid)) { + return last_uid = euid; + } + gid = getgid(); + if (uid_has_log_permission(gid)) { + return last_uid = gid; + } + gid = getegid(); + if (uid_has_log_permission(gid)) { + return last_uid = gid; + } + i = getgroups((size_t) 0, NULL); + if (i > 0) { + gid_t list[i]; + + getgroups(i, list); + while (--i >= 0) { + if (uid_has_log_permission(list[i])) { + return last_uid = list[i]; + } + } + } + return last_uid = uid; +} + +static int pmsgClear(struct android_log_logger *logger __unused, + struct android_log_transport_context *transp __unused) +{ + if (uid_has_log_permission(get_best_effective_uid())) { + return unlink("/sys/fs/pstore/pmsg-ramoops-0"); + } + errno = EPERM; + return -1; +} + +/* + * returns the logger version + */ +static int pmsgVersion(struct android_log_logger *logger __unused, + struct android_log_transport_context *transp __unused) +{ + return 4; +} + +static int pmsgRead(struct android_log_logger_list *logger_list, + struct android_log_transport_context *transp, + struct log_msg *log_msg) +{ + ssize_t ret; + off_t current, next; + uid_t uid; + struct android_log_logger *logger; + struct __attribute__((__packed__)) { + android_pmsg_log_header_t p; + android_log_header_t l; + } buf; + static uint8_t preread_count; + bool is_system; + + memset(log_msg, 0, sizeof(*log_msg)); + + if (transp->context.fd <= 0) { + int fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY); + + if (fd < 0) { + return -errno; + } + if (fd == 0) { /* Argggg */ + fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY); + close(0); + if (fd < 0) { + return -errno; + } + } + transp->context.fd = fd; + preread_count = 0; + } + + while(1) { + if (preread_count < sizeof(buf)) { + ret = TEMP_FAILURE_RETRY(read(transp->context.fd, + &buf.p.magic + preread_count, + sizeof(buf) - preread_count)); + if (ret < 0) { + return -errno; + } + preread_count += ret; + } + if (preread_count != sizeof(buf)) { + return preread_count ? -EIO : -EAGAIN; + } + if ((buf.p.magic != LOGGER_MAGIC) + || (buf.p.len <= sizeof(buf)) + || (buf.p.len > (sizeof(buf) + LOGGER_ENTRY_MAX_PAYLOAD)) + || (buf.l.id >= LOG_ID_MAX) + || (buf.l.realtime.tv_nsec >= NS_PER_SEC)) { + do { + memmove(&buf.p.magic, &buf.p.magic + 1, --preread_count); + } while (preread_count && (buf.p.magic != LOGGER_MAGIC)); + continue; + } + preread_count = 0; + + if ((transp->logMask & (1 << buf.l.id)) && + ((!logger_list->start.tv_sec && !logger_list->start.tv_nsec) || + ((logger_list->start.tv_sec <= buf.l.realtime.tv_sec) && + ((logger_list->start.tv_sec != buf.l.realtime.tv_sec) || + (logger_list->start.tv_nsec <= + buf.l.realtime.tv_nsec)))) && + (!logger_list->pid || (logger_list->pid == buf.p.pid))) { + uid = get_best_effective_uid(); + is_system = uid_has_log_permission(uid); + if (is_system || (uid == buf.p.uid)) { + ret = TEMP_FAILURE_RETRY(read(transp->context.fd, + is_system ? + log_msg->entry_v4.msg : + log_msg->entry_v3.msg, + buf.p.len - sizeof(buf))); + if (ret < 0) { + return -errno; + } + if (ret != (ssize_t)(buf.p.len - sizeof(buf))) { + return -EIO; + } + + log_msg->entry_v4.len = buf.p.len - sizeof(buf); + log_msg->entry_v4.hdr_size = is_system ? + sizeof(log_msg->entry_v4) : + sizeof(log_msg->entry_v3); + log_msg->entry_v4.pid = buf.p.pid; + log_msg->entry_v4.tid = buf.l.tid; + log_msg->entry_v4.sec = buf.l.realtime.tv_sec; + log_msg->entry_v4.nsec = buf.l.realtime.tv_nsec; + log_msg->entry_v4.lid = buf.l.id; + if (is_system) { + log_msg->entry_v4.uid = buf.p.uid; + } + + return ret; + } + } + + current = TEMP_FAILURE_RETRY(lseek(transp->context.fd, + (off_t)0, SEEK_CUR)); + if (current < 0) { + return -errno; + } + next = TEMP_FAILURE_RETRY(lseek(transp->context.fd, + (off_t)(buf.p.len - sizeof(buf)), + SEEK_CUR)); + if (next < 0) { + return -errno; + } + if ((next - current) != (ssize_t)(buf.p.len - sizeof(buf))) { + return -EIO; + } + } +} + +static void pmsgClose(struct android_log_logger_list *logger_list __unused, + struct android_log_transport_context *transp) { + if (transp->context.fd > 0) { + close (transp->context.fd); + } + transp->context.fd = 0; +} + +LIBLOG_ABI_PRIVATE ssize_t __android_log_pmsg_file_read( + log_id_t logId, + char prio, + const char *prefix, + __android_log_pmsg_file_read_fn fn, void *arg) { + ssize_t ret; + struct android_log_logger_list logger_list; + struct android_log_transport_context transp; + struct content { + struct listnode node; + union { + struct logger_entry_v4 entry; + struct logger_entry_v4 entry_v4; + struct logger_entry_v3 entry_v3; + struct logger_entry_v2 entry_v2; + struct logger_entry entry_v1; + }; + } *content; + struct names { + struct listnode node; + struct listnode content; + log_id_t id; + char prio; + char name[]; + } *names; + struct listnode name_list; + struct listnode *node, *n; + size_t len, prefix_len; + + if (!fn) { + return -EINVAL; + } + + /* Add just enough clues in logger_list and transp to make API function */ + memset(&logger_list, 0, sizeof(logger_list)); + memset(&transp, 0, sizeof(transp)); + + logger_list.mode = ANDROID_LOG_PSTORE | + ANDROID_LOG_NONBLOCK | + ANDROID_LOG_RDONLY; + transp.logMask = (unsigned)-1; + if (logId != LOG_ID_ANY) { + transp.logMask = (1 << logId); + } + transp.logMask &= ~((1 << LOG_ID_KERNEL) | + (1 << LOG_ID_EVENTS) | + (1 << LOG_ID_SECURITY)); + if (!transp.logMask) { + return -EINVAL; + } + + /* Initialize name list */ + list_init(&name_list); + + ret = SSIZE_MAX; + + /* Validate incoming prefix, shift until it contains only 0 or 1 : or / */ + prefix_len = 0; + if (prefix) { + const char *prev = NULL, *last = NULL, *cp = prefix; + while ((cp = strpbrk(cp, "/:"))) { + prev = last; + last = cp; + cp = cp + 1; + } + if (prev) { + prefix = prev + 1; + } + prefix_len = strlen(prefix); + } + + /* Read the file content */ + while (pmsgRead(&logger_list, &transp, &transp.logMsg) > 0) { + char *cp; + size_t hdr_size = transp.logMsg.entry.hdr_size ? + transp.logMsg.entry.hdr_size : sizeof(transp.logMsg.entry_v1); + char *msg = (char *)&transp.logMsg + hdr_size; + char *split = NULL; + + /* Check for invalid sequence number */ + if ((transp.logMsg.entry.nsec % ANDROID_LOG_PMSG_FILE_SEQUENCE) || + ((transp.logMsg.entry.nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >= + ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE)) { + continue; + } + + /* Determine if it has : format for tag */ + len = transp.logMsg.entry.len - sizeof(prio); + for (cp = msg + sizeof(prio); + *cp && isprint(*cp) && !isspace(*cp) && --len; + ++cp) { + if (*cp == ':') { + if (split) { + break; + } + split = cp; + } + } + if (*cp || !split) { + continue; + } + + /* Filters */ + if (prefix_len && strncmp(msg + sizeof(prio), prefix, prefix_len)) { + size_t offset; + /* + * Allow : to be a synonym for / + * Things we do dealing with const char * and do not alloc + */ + split = strchr(prefix, ':'); + if (split) { + continue; + } + split = strchr(prefix, '/'); + if (!split) { + continue; + } + offset = split - prefix; + if ((msg[offset + sizeof(prio)] != ':') || + strncmp(msg + sizeof(prio), prefix, offset)) { + continue; + } + ++offset; + if ((prefix_len > offset) && + strncmp(&msg[offset + sizeof(prio)], split + 1, prefix_len - offset)) { + continue; + } + } + + if ((prio != ANDROID_LOG_ANY) && (*msg < prio)) { + continue; + } + + /* check if there is an existing entry */ + list_for_each(node, &name_list) { + names = node_to_item(node, struct names, node); + if (!strcmp(names->name, msg + sizeof(prio)) && + (names->id == transp.logMsg.entry.lid) && + (names->prio == *msg)) { + break; + } + } + + /* We do not have an existing entry, create and add one */ + if (node == &name_list) { + static const char numbers[] = "0123456789"; + unsigned long long nl; + + len = strlen(msg + sizeof(prio)) + 1; + names = calloc(1, sizeof(*names) + len); + if (!names) { + ret = -ENOMEM; + break; + } + strcpy(names->name, msg + sizeof(prio)); + names->id = transp.logMsg.entry.lid; + names->prio = *msg; + list_init(&names->content); + /* + * Insert in reverse numeric _then_ alpha sorted order as + * representative of log rotation: + * + * log.10 + * klog.10 + * . . . + * log.2 + * klog.2 + * log.1 + * klog.1 + * log + * klog + * + * thus when we present the content, we are provided the oldest + * first, which when 'refreshed' could spill off the end of the + * pmsg FIFO but retaining the newest data for last with best + * chances to survive. + */ + nl = 0; + cp = strpbrk(names->name, numbers); + if (cp) { + nl = strtoull(cp, NULL, 10); + } + list_for_each_reverse(node, &name_list) { + struct names *a_name = node_to_item(node, struct names, node); + const char *r = a_name->name; + int compare = 0; + + unsigned long long nr = 0; + cp = strpbrk(r, numbers); + if (cp) { + nr = strtoull(cp, NULL, 10); + } + if (nr != nl) { + compare = (nl > nr) ? 1 : -1; + } + if (compare == 0) { + compare = strcmp(names->name, r); + } + if (compare <= 0) { + break; + } + } + list_add_head(node, &names->node); + } + + /* Remove any file fragments that match our sequence number */ + list_for_each_safe(node, n, &names->content) { + content = node_to_item(node, struct content, node); + if (transp.logMsg.entry.nsec == content->entry.nsec) { + list_remove(&content->node); + free(content); + } + } + + /* Add content */ + content = calloc(1, sizeof(content->node) + + hdr_size + transp.logMsg.entry.len); + if (!content) { + ret = -ENOMEM; + break; + } + memcpy(&content->entry, &transp.logMsg.entry, + hdr_size + transp.logMsg.entry.len); + + /* Insert in sequence number sorted order, to ease reconstruction */ + list_for_each_reverse(node, &names->content) { + if ((node_to_item(node, struct content, node))->entry.nsec < + transp.logMsg.entry.nsec) { + break; + } + } + list_add_head(node, &content->node); + } + pmsgClose(&logger_list, &transp); + + /* Progress through all the collected files */ + list_for_each_safe(node, n, &name_list) { + struct listnode *content_node, *m; + char *buf; + size_t sequence, tag_len; + + names = node_to_item(node, struct names, node); + + /* Construct content into a linear buffer */ + buf = NULL; + len = 0; + sequence = 0; + tag_len = strlen(names->name) + sizeof(char); /* tag + nul */ + list_for_each_safe(content_node, m, &names->content) { + ssize_t add_len; + + content = node_to_item(content_node, struct content, node); + add_len = content->entry.len - tag_len - sizeof(prio); + if (add_len <= 0) { + list_remove(content_node); + free(content); + continue; + } + + if (!buf) { + buf = malloc(sizeof(char)); + if (!buf) { + ret = -ENOMEM; + list_remove(content_node); + free(content); + continue; + } + *buf = '\0'; + } + + /* Missing sequence numbers */ + while (sequence < content->entry.nsec) { + /* plus space for enforced nul */ + buf = realloc(buf, len + sizeof(char) + sizeof(char)); + if (!buf) { + break; + } + buf[len] = '\f'; /* Mark missing content with a form feed */ + buf[++len] = '\0'; + sequence += ANDROID_LOG_PMSG_FILE_SEQUENCE; + } + if (!buf) { + ret = -ENOMEM; + list_remove(content_node); + free(content); + continue; + } + /* plus space for enforced nul */ + buf = realloc(buf, len + add_len + sizeof(char)); + if (!buf) { + ret = -ENOMEM; + list_remove(content_node); + free(content); + continue; + } + memcpy(buf + len, + (char *)&content->entry + content->entry.hdr_size + + tag_len + sizeof(prio), + add_len); + len += add_len; + buf[len] = '\0'; /* enforce trailing hidden nul */ + sequence = content->entry.nsec + ANDROID_LOG_PMSG_FILE_SEQUENCE; + + list_remove(content_node); + free(content); + } + if (buf) { + if (len) { + /* Buffer contains enforced trailing nul just beyond length */ + ssize_t r; + *strchr(names->name, ':') = '/'; /* Convert back to filename */ + r = (*fn)(names->id, names->prio, names->name, buf, len, arg); + if ((ret >= 0) && (r > 0)) { + if (ret == SSIZE_MAX) { + ret = r; + } else { + ret += r; + } + } else if (r < ret) { + ret = r; + } + } + free(buf); + } + list_remove(node); + free(names); + } + return (ret == SSIZE_MAX) ? -ENOENT : ret; +} diff --git a/liblog/pmsg_writer.c b/liblog/pmsg_writer.c new file mode 100644 index 000000000..7034cebaa --- /dev/null +++ b/liblog/pmsg_writer.c @@ -0,0 +1,270 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#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() +{ + if (pmsgLoggerWrite.context.fd < 0) { + pmsgLoggerWrite.context.fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY)); + } + + return pmsgLoggerWrite.context.fd; +} + +static void pmsgClose() +{ + if (pmsgLoggerWrite.context.fd >= 0) { + close(pmsgLoggerWrite.context.fd); + pmsgLoggerWrite.context.fd = -1; + } +} + +static int pmsgAvailable(log_id_t logId) +{ + if (logId > LOG_ID_SECURITY) { + return -EINVAL; + } + if (pmsgLoggerWrite.context.fd < 0) { + if (access("/dev/pmsg0", W_OK) == 0) { + return 0; + } + return -EBADF; + } + return 1; +} + +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 (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 = __android_log_pid(); + + 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(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 ":\0" 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 = :) */ +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) { + int fd; + 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; + } + + fd = pmsgLoggerWrite.context.fd; + if (fd < 0) { + __android_log_lock(); + fd = pmsgOpen(); + __android_log_unlock(); + if (fd < 0) { + return -EBADF; + } + } + + 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; + + 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; + + ret = pmsgWrite(logId, &ts, vec, sizeof(vec) / sizeof(vec[0])); + + if (ret <= 0) { + free(cp); + return ret; + } + length -= transfer; + buf += transfer; + } + free(cp); + return len; +} diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp index af3a3b432..c25bdbb3b 100644 --- a/liblog/tests/liblog_test.cpp +++ b/liblog/tests/liblog_test.cpp @@ -896,7 +896,10 @@ Good Signior Leonato, you are come to meet your\n\ trouble: the fashion of the world is to avoid\n\ cost, and you encounter it\n\ LEONATO\n\ -Never came trouble to my house in the likeness of your grace"; +Never came trouble to my house in the likeness of your grace,\n\ +for trouble being gone, comfort should remain, but\n\ +when you depart from me, sorrow abides and happiness\n\ +takes his leave."; TEST(liblog, max_payload) { pid_t pid = getpid(); @@ -2451,3 +2454,56 @@ TEST(liblog, create_android_logger_overflow) { EXPECT_LE(0, android_log_destroy(&ctx)); ASSERT_TRUE(NULL == ctx); } + +static const char __pmsg_file[] = + "/data/william-shakespeare/MuchAdoAboutNothing.txt"; + +TEST(liblog, __android_log_pmsg_file_write) { + EXPECT_LT(0, __android_log_pmsg_file_write( + LOG_ID_CRASH, ANDROID_LOG_VERBOSE, + __pmsg_file, max_payload_buf, sizeof(max_payload_buf))); + fprintf(stderr, "Reboot, ensure file %s matches\n" + "with liblog.__android_log_msg_file_read test\n", + __pmsg_file); +} + +ssize_t __pmsg_fn(log_id_t logId, char prio, const char *filename, + const char *buf, size_t len, void *arg) { + EXPECT_TRUE(NULL == arg); + EXPECT_EQ(LOG_ID_CRASH, logId); + EXPECT_EQ(ANDROID_LOG_VERBOSE, prio); + EXPECT_FALSE(NULL == strstr(__pmsg_file, filename)); + EXPECT_EQ(len, sizeof(max_payload_buf)); + EXPECT_EQ(0, strcmp(max_payload_buf, buf)); + + ++signaled; + if ((len != sizeof(max_payload_buf)) || + strcmp(max_payload_buf, buf)) { + fprintf(stderr, "comparison fails on content \"%s\"\n", buf); + } + return !arg || + (LOG_ID_CRASH != logId) || + (ANDROID_LOG_VERBOSE != prio) || + !strstr(__pmsg_file, filename) || + (len != sizeof(max_payload_buf)) || + !!strcmp(max_payload_buf, buf) ? -ENOEXEC : 1; +} + +TEST(liblog, __android_log_pmsg_file_read) { + signaled = 0; + + ssize_t ret = __android_log_pmsg_file_read( + LOG_ID_CRASH, ANDROID_LOG_VERBOSE, + __pmsg_file, __pmsg_fn, NULL); + + if (ret == -ENOENT) { + fprintf(stderr, + "No pre-boot results of liblog.__android_log_mesg_file_write to " + "compare with,\n" + "false positive test result.\n"); + return; + } + + EXPECT_LT(0, ret); + EXPECT_EQ(1U, signaled); +} diff --git a/liblog/uio.c b/liblog/uio.c index d0184dc90..ac0558f3f 100644 --- a/liblog/uio.c +++ b/liblog/uio.c @@ -20,7 +20,7 @@ #include -#include "log_cdefs.h" +#include "log_portability.h" LIBLOG_ABI_PUBLIC int readv(int fd, struct iovec *vecs, int count) {