/* * Copyright (C) 2017 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. */ /* * stderr write handler. Output is logcat-like, and responds to * logcat's environment variables ANDROID_PRINTF_LOG and * ANDROID_LOG_TAGS to filter output. * * This transport only provides a writer, that means that it does not * provide an End-To-End capability as the logs are effectively _lost_ * to the stderr file stream. The purpose of this transport is to * supply a means for command line tools to report their logging * to the stderr stream, in line with all other activities. */ #include #include #include #include #include #include #include #include #include #include #include #include "log_portability.h" #include "logger.h" static int stderrOpen(); static void stderrClose(); static int stderrAvailable(log_id_t logId); static int stderrWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr); struct stderrContext { AndroidLogFormat* logformat; #if defined(__ANDROID__) EventTagMap* eventTagMap; #endif }; LIBLOG_HIDDEN struct android_log_transport_write stderrLoggerWrite = { .node = { &stderrLoggerWrite.node, &stderrLoggerWrite.node }, .context.priv = NULL, .name = "stderr", .available = stderrAvailable, .open = stderrOpen, .close = stderrClose, .write = stderrWrite, }; static int stderrOpen() { struct stderrContext* ctx; const char* envStr; bool setFormat; if (!stderr || (fileno(stderr) < 0)) { return -EBADF; } if (stderrLoggerWrite.context.priv) { return fileno(stderr); } ctx = calloc(1, sizeof(struct stderrContext)); if (!ctx) { return -ENOMEM; } ctx->logformat = android_log_format_new(); if (!ctx->logformat) { free(ctx); return -ENOMEM; } envStr = getenv("ANDROID_PRINTF_LOG"); setFormat = false; if (envStr) { char* formats = strdup(envStr); char* sv = NULL; char* arg = formats; while (!!(arg = strtok_r(arg, ",:; \t\n\r\f", &sv))) { AndroidLogPrintFormat format = android_log_formatFromString(arg); arg = NULL; if (format == FORMAT_OFF) { continue; } if (android_log_setPrintFormat(ctx->logformat, format) <= 0) { continue; } setFormat = true; } free(formats); } if (!setFormat) { AndroidLogPrintFormat format = android_log_formatFromString("threadtime"); android_log_setPrintFormat(ctx->logformat, format); } envStr = getenv("ANDROID_LOG_TAGS"); if (envStr) { android_log_addFilterString(ctx->logformat, envStr); } stderrLoggerWrite.context.priv = ctx; return fileno(stderr); } static void stderrClose() { struct stderrContext* ctx = stderrLoggerWrite.context.priv; if (ctx) { stderrLoggerWrite.context.priv = NULL; if (ctx->logformat) { android_log_format_free(ctx->logformat); ctx->logformat = NULL; } #if defined(__ANDROID__) if (ctx->eventTagMap) { android_closeEventTagMap(ctx->eventTagMap); ctx->eventTagMap = NULL; } #endif } } static int stderrAvailable(log_id_t logId) { if ((logId >= LOG_ID_MAX) || (logId == LOG_ID_KERNEL)) { return -EINVAL; } return 1; } static int stderrWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) { struct log_msg log_msg; AndroidLogEntry entry; char binaryMsgBuf[1024]; int err; size_t i; struct stderrContext* ctx = stderrLoggerWrite.context.priv; if (!ctx) return -EBADF; if (!vec || !nr) return -EINVAL; log_msg.entry.len = 0; log_msg.entry.hdr_size = sizeof(log_msg.entry); log_msg.entry.pid = getpid(); #ifdef __BIONIC__ log_msg.entry.tid = gettid(); #else log_msg.entry.tid = getpid(); #endif log_msg.entry.sec = ts->tv_sec; log_msg.entry.nsec = ts->tv_nsec; log_msg.entry.lid = logId; log_msg.entry.uid = __android_log_uid(); for (i = 0; i < nr; ++i) { size_t len = vec[i].iov_len; if ((log_msg.entry.len + len) > LOGGER_ENTRY_MAX_PAYLOAD) { len = LOGGER_ENTRY_MAX_PAYLOAD - log_msg.entry.len; } if (!len) continue; memcpy(log_msg.entry.msg + log_msg.entry.len, vec[i].iov_base, len); log_msg.entry.len += len; } if ((logId == LOG_ID_EVENTS) || (logId == LOG_ID_SECURITY)) { #if defined(__ANDROID__) if (!ctx->eventTagMap) { ctx->eventTagMap = android_openEventTagMap(NULL); } #endif err = android_log_processBinaryLogBuffer(&log_msg.entry_v1, &entry, #if defined(__ANDROID__) ctx->eventTagMap, #else NULL, #endif binaryMsgBuf, sizeof(binaryMsgBuf)); } else { err = android_log_processLogBuffer(&log_msg.entry_v1, &entry); } /* print known truncated data, in essence logcat --debug */ if ((err < 0) && !entry.message) return -EINVAL; if (!android_log_shouldPrintLine(ctx->logformat, entry.tag, entry.priority)) { return log_msg.entry.len; } err = android_log_printLogLine(ctx->logformat, fileno(stderr), &entry); if (err < 0) return errno ? -errno : -EINVAL; return log_msg.entry.len; }