| /* | |
| * 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 <stdatomic.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <sys/time.h> | |
| #ifdef __BIONIC__ | |
| #include <android/set_abort_message.h> | |
| #endif | |
| #include <log/event_tag_map.h> | |
| #include <log/log_transport.h> | |
| #include <private/android_filesystem_config.h> | |
| #include <private/android_logger.h> | |
| #include "config_read.h" /* __android_log_config_read_close() definition */ | |
| #include "config_write.h" | |
| #include "log_portability.h" | |
| #include "logger.h" | |
| #define LOG_BUF_SIZE 1024 | |
| 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; | |
| /* | |
| * 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 | |
| * the simulator rather than a desktop tool and want to use the device. | |
| */ | |
| static enum { | |
| kLogUninitialized, | |
| kLogNotAvailable, | |
| kLogAvailable | |
| } g_log_status = kLogUninitialized; | |
| static int check_log_uid_permissions() { | |
| #if defined(__ANDROID__) | |
| uid_t uid = __android_log_uid(); | |
| /* Matches clientHasLogCredentials() in logd */ | |
| 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)) { | |
| gid = getegid(); | |
| if ((gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) { | |
| int num_groups; | |
| gid_t* groups; | |
| num_groups = getgroups(0, NULL); | |
| if (num_groups <= 0) { | |
| return -EPERM; | |
| } | |
| groups = calloc(num_groups, sizeof(gid_t)); | |
| if (!groups) { | |
| return -ENOMEM; | |
| } | |
| num_groups = getgroups(num_groups, groups); | |
| while (num_groups > 0) { | |
| if (groups[num_groups - 1] == AID_LOG) { | |
| break; | |
| } | |
| --num_groups; | |
| } | |
| free(groups); | |
| if (num_groups <= 0) { | |
| return -EPERM; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| #endif | |
| return 0; | |
| } | |
| static void __android_log_cache_available( | |
| struct android_log_transport_write* node) { | |
| size_t i; | |
| if (node->logMask) { | |
| return; | |
| } | |
| for (i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) { | |
| if (node->write && (i != LOG_ID_KERNEL) && | |
| ((i != LOG_ID_SECURITY) || (check_log_uid_permissions() == 0)) && | |
| (!node->available || ((*node->available)(i) >= 0))) { | |
| node->logMask |= 1 << i; | |
| } | |
| } | |
| } | |
| LIBLOG_ABI_PUBLIC int __android_log_dev_available() { | |
| struct android_log_transport_write* node; | |
| if (list_empty(&__android_log_transport_write)) { | |
| return kLogUninitialized; | |
| } | |
| write_transport_for_each(node, &__android_log_transport_write) { | |
| __android_log_cache_available(node); | |
| if (node->logMask) { | |
| return kLogAvailable; | |
| } | |
| } | |
| return kLogNotAvailable; | |
| } | |
| #if defined(__ANDROID__) | |
| static atomic_uintptr_t tagMap; | |
| #endif | |
| /* | |
| * Release any logger resources. A new log write will immediately re-acquire. | |
| */ | |
| LIBLOG_ABI_PUBLIC void __android_log_close() { | |
| struct android_log_transport_write* transport; | |
| #if defined(__ANDROID__) | |
| EventTagMap* m; | |
| #endif | |
| __android_log_lock(); | |
| write_to_log = __write_to_log_init; | |
| /* | |
| * Threads that are actively writing at this point are not held back | |
| * by a lock and are at risk of dropping the messages with a return code | |
| * -EBADF. Prefer to return error code than add the overhead of a lock to | |
| * each log writing call to guarantee delivery. In addition, anyone | |
| * calling this is doing so to release the logging resources and shut down, | |
| * for them to do so with outstanding log requests in other threads is a | |
| * disengenuous use of this function. | |
| */ | |
| write_transport_for_each(transport, &__android_log_persist_write) { | |
| if (transport->close) { | |
| (*transport->close)(); | |
| } | |
| } | |
| write_transport_for_each(transport, &__android_log_transport_write) { | |
| if (transport->close) { | |
| (*transport->close)(); | |
| } | |
| } | |
| __android_log_config_write_close(); | |
| #if defined(__ANDROID__) | |
| /* | |
| * Additional risk here somewhat mitigated by immediately unlock flushing | |
| * the processor cache. The multi-threaded race that we choose to accept, | |
| * to minimize locking, is an atomic_load in a writer picking up a value | |
| * just prior to entering this routine. There will be an use after free. | |
| * | |
| * Again, anyone calling this is doing so to release the logging resources | |
| * is most probably going to quiesce then shut down; or to restart after | |
| * a fork so the risk should be non-existent. For this reason we | |
| * choose a mitigation stance for efficiency instead of incuring the cost | |
| * of a lock for every log write. | |
| */ | |
| m = (EventTagMap*)atomic_exchange(&tagMap, (uintptr_t)0); | |
| #endif | |
| __android_log_unlock(); | |
| #if defined(__ANDROID__) | |
| if (m != (EventTagMap*)(uintptr_t)-1LL) android_closeEventTagMap(m); | |
| #endif | |
| } | |
| /* log_init_lock assumed */ | |
| static int __write_to_log_initialize() { | |
| struct android_log_transport_write* transport; | |
| struct listnode* n; | |
| int i = 0, ret = 0; | |
| __android_log_config_write(); | |
| write_transport_for_each_safe(transport, n, &__android_log_transport_write) { | |
| __android_log_cache_available(transport); | |
| if (!transport->logMask) { | |
| list_remove(&transport->node); | |
| continue; | |
| } | |
| 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) { | |
| __android_log_cache_available(transport); | |
| if (!transport->logMask) { | |
| list_remove(&transport->node); | |
| continue; | |
| } | |
| if (!transport->open || ((*transport->open)() < 0)) { | |
| if (transport->close) { | |
| (*transport->close)(); | |
| } | |
| list_remove(&transport->node); | |
| continue; | |
| } | |
| ++i; | |
| } | |
| if (!ret && !i) { | |
| return -ENODEV; | |
| } | |
| 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) { | |
| struct android_log_transport_write* node; | |
| int ret; | |
| struct timespec ts; | |
| size_t len, i; | |
| for (len = i = 0; i < nr; ++i) { | |
| len += vec[i].iov_len; | |
| } | |
| if (!len) { | |
| return -EINVAL; | |
| } | |
| #if defined(__ANDROID__) | |
| clock_gettime(android_log_clockid(), &ts); | |
| if (log_id == LOG_ID_SECURITY) { | |
| if (vec[0].iov_len < 4) { | |
| return -EINVAL; | |
| } | |
| ret = check_log_uid_permissions(); | |
| if (ret < 0) { | |
| return ret; | |
| } | |
| if (!__android_log_security()) { | |
| /* If only we could reset downstream logd counter */ | |
| return -EPERM; | |
| } | |
| } else if (log_id == LOG_ID_EVENTS) { | |
| const char* tag; | |
| size_t len; | |
| EventTagMap *m, *f; | |
| if (vec[0].iov_len < 4) { | |
| return -EINVAL; | |
| } | |
| tag = NULL; | |
| len = 0; | |
| f = NULL; | |
| m = (EventTagMap*)atomic_load(&tagMap); | |
| if (!m) { | |
| ret = __android_log_trylock(); | |
| m = (EventTagMap*)atomic_load(&tagMap); /* trylock flush cache */ | |
| if (!m) { | |
| m = android_openEventTagMap(NULL); | |
| if (ret) { /* trylock failed, use local copy, mark for close */ | |
| f = m; | |
| } else { | |
| if (!m) { /* One chance to open map file */ | |
| m = (EventTagMap*)(uintptr_t)-1LL; | |
| } | |
| atomic_store(&tagMap, (uintptr_t)m); | |
| } | |
| } | |
| if (!ret) { /* trylock succeeded, unlock */ | |
| __android_log_unlock(); | |
| } | |
| } | |
| if (m && (m != (EventTagMap*)(uintptr_t)-1LL)) { | |
| tag = android_lookupEventTag_len(m, &len, get4LE(vec[0].iov_base)); | |
| } | |
| ret = __android_log_is_loggable_len(ANDROID_LOG_INFO, tag, len, | |
| ANDROID_LOG_VERBOSE); | |
| if (f) { /* local copy marked for close */ | |
| android_closeEventTagMap(f); | |
| } | |
| if (!ret) { | |
| return -EPERM; | |
| } | |
| } else { | |
| /* Validate the incoming tag, tag content can not split across iovec */ | |
| char prio = ANDROID_LOG_VERBOSE; | |
| const char* tag = vec[0].iov_base; | |
| size_t len = vec[0].iov_len; | |
| if (!tag) { | |
| len = 0; | |
| } | |
| if (len > 0) { | |
| prio = *tag; | |
| if (len > 1) { | |
| --len; | |
| ++tag; | |
| } else { | |
| len = vec[1].iov_len; | |
| tag = ((const char*)vec[1].iov_base); | |
| if (!tag) { | |
| len = 0; | |
| } | |
| } | |
| } | |
| /* tag must be nul terminated */ | |
| if (tag && strnlen(tag, len) >= len) { | |
| tag = NULL; | |
| } | |
| if (!__android_log_is_loggable_len(prio, tag, len - 1, ANDROID_LOG_VERBOSE)) { | |
| return -EPERM; | |
| } | |
| } | |
| #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; | |
| i = 1 << log_id; | |
| write_transport_for_each(node, &__android_log_transport_write) { | |
| if (node->logMask & i) { | |
| 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->logMask & i) { | |
| (void)(*node->write)(log_id, &ts, vec, nr); | |
| } | |
| } | |
| return ret; | |
| } | |
| static int __write_to_log_init(log_id_t log_id, struct iovec* vec, size_t nr) { | |
| __android_log_lock(); | |
| if (write_to_log == __write_to_log_init) { | |
| int ret; | |
| ret = __write_to_log_initialize(); | |
| if (ret < 0) { | |
| __android_log_unlock(); | |
| if (!list_empty(&__android_log_persist_write)) { | |
| __write_to_log_daemon(log_id, vec, nr); | |
| } | |
| return ret; | |
| } | |
| write_to_log = __write_to_log_daemon; | |
| } | |
| __android_log_unlock(); | |
| return write_to_log(log_id, vec, nr); | |
| } | |
| LIBLOG_ABI_PUBLIC int __android_log_write(int prio, const char* tag, | |
| const char* msg) { | |
| return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg); | |
| } | |
| LIBLOG_ABI_PUBLIC int __android_log_buf_write(int bufID, int prio, | |
| const char* tag, const char* msg) { | |
| struct iovec vec[3]; | |
| char tmp_tag[32]; | |
| if (!tag) tag = ""; | |
| /* XXX: This needs to go! */ | |
| if (bufID != LOG_ID_RADIO) { | |
| switch (tag[0]) { | |
| case 'H': | |
| if (strcmp(tag + 1, "HTC_RIL" + 1)) break; | |
| goto inform; | |
| case 'R': | |
| /* Any log tag with "RIL" as the prefix */ | |
| if (strncmp(tag + 1, "RIL" + 1, strlen("RIL") - 1)) break; | |
| goto inform; | |
| case 'Q': | |
| /* Any log tag with "QC_RIL" as the prefix */ | |
| if (strncmp(tag + 1, "QC_RIL" + 1, strlen("QC_RIL") - 1)) break; | |
| goto inform; | |
| case 'I': | |
| /* Any log tag with "IMS" as the prefix */ | |
| if (strncmp(tag + 1, "IMS" + 1, strlen("IMS") - 1)) break; | |
| goto inform; | |
| case 'A': | |
| if (strcmp(tag + 1, "AT" + 1)) break; | |
| goto inform; | |
| case 'G': | |
| if (strcmp(tag + 1, "GSM" + 1)) break; | |
| goto inform; | |
| case 'S': | |
| if (strcmp(tag + 1, "STK" + 1) && strcmp(tag + 1, "SMS" + 1)) break; | |
| goto inform; | |
| case 'C': | |
| if (strcmp(tag + 1, "CDMA" + 1)) break; | |
| goto inform; | |
| case 'P': | |
| if (strcmp(tag + 1, "PHONE" + 1)) break; | |
| /* FALLTHRU */ | |
| inform: | |
| bufID = LOG_ID_RADIO; | |
| snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag); | |
| tag = tmp_tag; | |
| /* FALLTHRU */ | |
| default: | |
| break; | |
| } | |
| } | |
| #if __BIONIC__ | |
| if (prio == ANDROID_LOG_FATAL) { | |
| android_set_abort_message(msg); | |
| } | |
| #endif | |
| vec[0].iov_base = (unsigned char*)&prio; | |
| vec[0].iov_len = 1; | |
| vec[1].iov_base = (void*)tag; | |
| vec[1].iov_len = strlen(tag) + 1; | |
| vec[2].iov_base = (void*)msg; | |
| vec[2].iov_len = strlen(msg) + 1; | |
| return write_to_log(bufID, vec, 3); | |
| } | |
| LIBLOG_ABI_PUBLIC int __android_log_vprint(int prio, const char* tag, | |
| const char* fmt, va_list ap) { | |
| char buf[LOG_BUF_SIZE]; | |
| vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); | |
| return __android_log_write(prio, tag, buf); | |
| } | |
| LIBLOG_ABI_PUBLIC int __android_log_print(int prio, const char* tag, | |
| const char* fmt, ...) { | |
| va_list ap; | |
| char buf[LOG_BUF_SIZE]; | |
| va_start(ap, fmt); | |
| vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); | |
| va_end(ap); | |
| return __android_log_write(prio, tag, buf); | |
| } | |
| LIBLOG_ABI_PUBLIC int __android_log_buf_print(int bufID, int prio, | |
| const char* tag, const char* fmt, | |
| ...) { | |
| va_list ap; | |
| char buf[LOG_BUF_SIZE]; | |
| va_start(ap, fmt); | |
| vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); | |
| va_end(ap); | |
| 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, ...) { | |
| char buf[LOG_BUF_SIZE]; | |
| if (fmt) { | |
| va_list ap; | |
| va_start(ap, fmt); | |
| vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); | |
| va_end(ap); | |
| } else { | |
| /* Msg not provided, log condition. N.B. Do not use cond directly as | |
| * format string as it could contain spurious '%' syntax (e.g. | |
| * "%d" in "blocks%devs == 0"). | |
| */ | |
| if (cond) | |
| snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond); | |
| else | |
| strcpy(buf, "Unspecified assertion failed"); | |
| } | |
| // Log assertion failures to stderr for the benefit of "adb shell" users | |
| // and gtests (http://b/23675822). | |
| struct iovec iov[2] = { | |
| { buf, strlen(buf) }, { (char*)"\n", 1 }, | |
| }; | |
| TEMP_FAILURE_RETRY(writev(2, iov, 2)); | |
| __android_log_write(ANDROID_LOG_FATAL, tag, buf); | |
| abort(); /* abort so we have a chance to debug the situation */ | |
| /* NOTREACHED */ | |
| } | |
| LIBLOG_ABI_PUBLIC int __android_log_bwrite(int32_t tag, const void* payload, | |
| size_t len) { | |
| struct iovec vec[2]; | |
| vec[0].iov_base = &tag; | |
| vec[0].iov_len = sizeof(tag); | |
| vec[1].iov_base = (void*)payload; | |
| vec[1].iov_len = len; | |
| return write_to_log(LOG_ID_EVENTS, vec, 2); | |
| } | |
| LIBLOG_ABI_PUBLIC int __android_log_security_bwrite(int32_t tag, | |
| const void* payload, | |
| size_t len) { | |
| struct iovec vec[2]; | |
| vec[0].iov_base = &tag; | |
| vec[0].iov_len = sizeof(tag); | |
| vec[1].iov_base = (void*)payload; | |
| vec[1].iov_len = len; | |
| return write_to_log(LOG_ID_SECURITY, vec, 2); | |
| } | |
| /* | |
| * Like __android_log_bwrite, but takes the type as well. Doesn't work | |
| * for the general case where we're generating lists of stuff, but very | |
| * handy if we just want to dump an integer into the log. | |
| */ | |
| LIBLOG_ABI_PUBLIC int __android_log_btwrite(int32_t tag, char type, | |
| const void* payload, size_t len) { | |
| struct iovec vec[3]; | |
| vec[0].iov_base = &tag; | |
| vec[0].iov_len = sizeof(tag); | |
| vec[1].iov_base = &type; | |
| vec[1].iov_len = sizeof(type); | |
| vec[2].iov_base = (void*)payload; | |
| vec[2].iov_len = len; | |
| return write_to_log(LOG_ID_EVENTS, vec, 3); | |
| } | |
| /* | |
| * Like __android_log_bwrite, but used for writing strings to the | |
| * event log. | |
| */ | |
| LIBLOG_ABI_PUBLIC int __android_log_bswrite(int32_t tag, const char* payload) { | |
| struct iovec vec[4]; | |
| char type = EVENT_TYPE_STRING; | |
| uint32_t len = strlen(payload); | |
| vec[0].iov_base = &tag; | |
| vec[0].iov_len = sizeof(tag); | |
| vec[1].iov_base = &type; | |
| vec[1].iov_len = sizeof(type); | |
| vec[2].iov_base = &len; | |
| vec[2].iov_len = sizeof(len); | |
| vec[3].iov_base = (void*)payload; | |
| vec[3].iov_len = len; | |
| return write_to_log(LOG_ID_EVENTS, vec, 4); | |
| } | |
| /* | |
| * Like __android_log_security_bwrite, but used for writing strings to the | |
| * security log. | |
| */ | |
| LIBLOG_ABI_PUBLIC int __android_log_security_bswrite(int32_t tag, | |
| const char* payload) { | |
| struct iovec vec[4]; | |
| char type = EVENT_TYPE_STRING; | |
| uint32_t len = strlen(payload); | |
| vec[0].iov_base = &tag; | |
| vec[0].iov_len = sizeof(tag); | |
| vec[1].iov_base = &type; | |
| vec[1].iov_len = sizeof(type); | |
| vec[2].iov_base = &len; | |
| vec[2].iov_len = sizeof(len); | |
| vec[3].iov_base = (void*)payload; | |
| vec[3].iov_len = len; | |
| return write_to_log(LOG_ID_SECURITY, vec, 4); | |
| } | |
| static int __write_to_log_null(log_id_t log_id, struct iovec* vec, size_t nr) { | |
| size_t len, i; | |
| if ((log_id < LOG_ID_MIN) || (log_id >= LOG_ID_MAX)) { | |
| return -EINVAL; | |
| } | |
| for (len = i = 0; i < nr; ++i) { | |
| len += vec[i].iov_len; | |
| } | |
| if (!len) { | |
| return -EINVAL; | |
| } | |
| return len; | |
| } | |
| /* Following functions need access to our internal write_to_log status */ | |
| LIBLOG_HIDDEN int __android_log_transport; | |
| LIBLOG_ABI_PUBLIC int android_set_log_transport(int transport_flag) { | |
| int retval; | |
| if (transport_flag < 0) { | |
| return -EINVAL; | |
| } | |
| retval = LOGGER_NULL; | |
| __android_log_lock(); | |
| if (transport_flag & LOGGER_NULL) { | |
| write_to_log = __write_to_log_null; | |
| __android_log_unlock(); | |
| return retval; | |
| } | |
| __android_log_transport &= LOGGER_LOCAL | LOGGER_LOGD | LOGGER_STDERR; | |
| transport_flag &= LOGGER_LOCAL | LOGGER_LOGD | LOGGER_STDERR; | |
| if (__android_log_transport != transport_flag) { | |
| __android_log_transport = transport_flag; | |
| __android_log_config_write_close(); | |
| __android_log_config_read_close(); | |
| write_to_log = __write_to_log_init; | |
| /* generically we only expect these two values for write_to_log */ | |
| } else if ((write_to_log != __write_to_log_init) && | |
| (write_to_log != __write_to_log_daemon)) { | |
| write_to_log = __write_to_log_init; | |
| } | |
| retval = __android_log_transport; | |
| __android_log_unlock(); | |
| return retval; | |
| } | |
| LIBLOG_ABI_PUBLIC int android_get_log_transport() { | |
| int ret = LOGGER_DEFAULT; | |
| __android_log_lock(); | |
| if (write_to_log == __write_to_log_null) { | |
| ret = LOGGER_NULL; | |
| } else { | |
| __android_log_transport &= LOGGER_LOCAL | LOGGER_LOGD | LOGGER_STDERR; | |
| ret = __android_log_transport; | |
| if ((write_to_log != __write_to_log_init) && | |
| (write_to_log != __write_to_log_daemon)) { | |
| ret = -EINVAL; | |
| } | |
| } | |
| __android_log_unlock(); | |
| return ret; | |
| } |