Merge changes from topic '27176738-pmsg-write-core'

* changes:
  liblog: add __android_log_pmsg_file_read
  liblog: add __android_log_pmsg_file_write
  liblog: split out transports into separate files
This commit is contained in:
Mark Salyzyn 2016-03-22 20:59:11 +00:00 committed by Gerrit Code Review
commit 593a5c1573
29 changed files with 3138 additions and 1255 deletions

View File

@ -19,7 +19,10 @@
#ifndef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
#define _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
/* Android private interfaces */
#include <stdint.h>
#include <sys/types.h>
#include <log/log.h>
#include <log/log_read.h>
@ -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_ */

View File

@ -15,20 +15,29 @@
//
liblog_sources = [
"logd_write.c",
"log_event_list.c",
"log_event_write.c",
"logger_write.c",
"config_write.c",
"logger_name.c",
"logger_lock.c",
]
liblog_host_sources = [
"fake_log_device.c",
//"event.logtags",
"fake_writer.c",
]
liblog_target_sources = [
"event_tag_map.c",
"config_read.c",
"log_time.cpp",
"log_is_loggable.c",
"logprint.c",
"log_read.c",
"pmsg_reader.c",
"pmsg_writer.c",
"logd_reader.c",
"logd_writer.c",
"logger_read.c",
]
// Shared and static library for host and device

View File

@ -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
# ========================================================

62
liblog/config_read.c Normal file
View File

@ -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
}

50
liblog/config_read.h Normal file
View File

@ -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 <cutils/list.h>
#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__ */

66
liblog/config_write.c Normal file
View File

@ -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
}

50
liblog/config_write.h Normal file
View File

@ -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 <cutils/list.h>
#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__ */

View File

@ -24,7 +24,7 @@
#include <log/event_tag_map.h>
#include <log/log.h>
#include "log_cdefs.h"
#include "log_portability.h"
#define OUT_TAG "EventTagMap"

View File

@ -31,7 +31,7 @@
#include <log/logd.h>
#include "fake_log_device.h"
#include "log_cdefs.h"
#include "log_portability.h"
#define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */

View File

@ -19,7 +19,7 @@
#include <sys/types.h>
#include "log_cdefs.h"
#include "log_portability.h"
struct iovec;

82
liblog/fake_writer.c Normal file
View File

@ -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 <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <log/log.h>
#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;
}

View File

@ -25,7 +25,7 @@
#include <log/log.h>
#include <log/logger.h>
#include "log_cdefs.h"
#include "log_portability.h"
#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))

View File

@ -18,7 +18,7 @@
#include <log/log.h>
#include "log_cdefs.h"
#include "log_portability.h"
#define MAX_SUBTAG_LEN 32

View File

@ -23,7 +23,7 @@
#include <android/log.h>
#include "log_cdefs.h"
#include "log_portability.h"
static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER;

View File

@ -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 <sys/cdefs.h>
#include <unistd.h>
/* 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__ */

View File

@ -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 <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <poll.h>
#include <signal.h>
#include <stdbool.h>
#include <stddef.h>
#define NOMINMAX /* for windows to suppress definition of min in stdlib.h */
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <cutils/list.h>
#include <cutils/sockets.h>
#include <log/log.h>
#include <log/logger.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#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 <sys/socket.h>
#include <sys/un.h>
#include <sys/select.h>
#include <sys/types.h>
/* Private copy of ../libcutils/socket_local.h prevent library loops */
#define FILESYSTEM_SOCKET_PREFIX "/tmp/"
#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/"
/* End of ../libcutils/socket_local.h */
#define LISTEN_BACKLOG 4
/* Documented in header file. */
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);
}

View File

@ -21,7 +21,7 @@
#include <log/log_read.h>
#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 };

670
liblog/logd_reader.c Normal file
View File

@ -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 <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <poll.h>
#include <stdarg.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <time.h>
#include <unistd.h>
#include <cutils/sockets.h>
#include <log/logd.h>
#include <log/logger.h>
#include <log/log_read.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#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 <sys/socket.h>
#include <sys/un.h>
#include <sys/select.h>
#include <sys/types.h>
/* Private copy of ../libcutils/socket_local.h prevent library loops */
#define FILESYSTEM_SOCKET_PREFIX "/tmp/"
#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/"
/* End of ../libcutils/socket_local.h */
#define LISTEN_BACKLOG 4
/* Documented in header file. */
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;
}
}

267
liblog/logd_writer.c Normal file
View File

@ -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 <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <poll.h>
#include <stdarg.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <time.h>
#include <unistd.h>
#include <cutils/sockets.h>
#include <log/logd.h>
#include <log/logger.h>
#include <log/log_read.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#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;
}

159
liblog/logger.h Normal file
View File

@ -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 <stdbool.h>
#include <log/uio.h>
#include <cutils/list.h>
#include <log/log.h>
#include <log/log_read.h>
#include <log/logger.h>
#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__ */

82
liblog/logger_lock.c Normal file
View File

@ -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 <pthread.h>
#endif
#include <private/android_filesystem_config.h>
#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
}

65
liblog/logger_name.c Normal file
View File

@ -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 <string.h>
#include <log/log.h>
#include <log/logger.h>
#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 */
}

474
liblog/logger_read.c Normal file
View File

@ -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 <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <sched.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <cutils/list.h>
#include <log/log.h>
#include <log/logger.h>
#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);
}

View File

@ -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 <endian.h>
#endif
#include <errno.h>
#include <fcntl.h>
#if !defined(_WIN32)
#include <pthread.h>
#endif
#include <stdarg.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#if (FAKE_LOG_DEVICE == 0)
#include <sys/socket.h>
#include <sys/un.h>
#endif
#include <time.h>
#include <unistd.h>
#include <sys/time.h>
#ifdef __BIONIC__
#include <android/set_abort_message.h>
@ -46,55 +31,15 @@
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#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];

View File

@ -34,7 +34,7 @@
#include <log/logprint.h>
#include <private/android_filesystem_config.h>
#include "log_cdefs.h"
#include "log_portability.h"
#define MS_PER_NSEC 1000000
#define US_PER_NSEC 1000

587
liblog/pmsg_reader.c Normal file
View File

@ -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 <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#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 <dirbase>:<filebase> 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;
}

270
liblog/pmsg_writer.c Normal file
View File

@ -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 <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <log/log.h>
#include <log/logger.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#include "config_write.h"
#include "log_portability.h"
#include "logger.h"
static int pmsgOpen();
static void pmsgClose();
static int pmsgAvailable(log_id_t logId);
static int pmsgWrite(log_id_t logId, struct timespec *ts,
struct iovec *vec, size_t nr);
LIBLOG_HIDDEN struct android_log_transport_write pmsgLoggerWrite = {
.node = { &pmsgLoggerWrite.node, &pmsgLoggerWrite.node },
.context.fd = -1,
.name = "pmsg",
.available = pmsgAvailable,
.open = pmsgOpen,
.close = pmsgClose,
.write = pmsgWrite,
};
static int pmsgOpen()
{
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 "<basedir>:<basefile>\0<content>" to a
* maximum of LOGGER_ENTRY_MAX_PAYLOAD, but scaled to the last newline in the
* file.
*
* Will hijack the header.realtime.tv_nsec field for a sequence number in usec.
*/
static inline const char *strnrchr(const char *buf, size_t len, char c) {
const char *cp = buf + len;
while ((--cp > buf) && (*cp != c));
if (cp <= buf) {
return buf + len;
}
return cp;
}
/* Write a buffer as filename references (tag = <basedir>:<basename>) */
LIBLOG_ABI_PRIVATE ssize_t __android_log_pmsg_file_write(
log_id_t logId,
char prio,
const char *filename,
const char *buf, size_t len) {
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;
}

View File

@ -957,7 +957,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();
@ -2520,3 +2523,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);
}

View File

@ -20,7 +20,7 @@
#include <log/uio.h>
#include "log_cdefs.h"
#include "log_portability.h"
LIBLOG_ABI_PUBLIC int readv(int fd, struct iovec *vecs, int count)
{