diff options
Diffstat (limited to 'logd/LogTags.cpp')
-rw-r--r-- | logd/LogTags.cpp | 903 |
1 files changed, 903 insertions, 0 deletions
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp new file mode 100644 index 000000000..0cc7886ea --- /dev/null +++ b/logd/LogTags.cpp @@ -0,0 +1,903 @@ +/* + * 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. + */ + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <pthread.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <string> + +#include <android-base/file.h> +#include <android-base/macros.h> +#include <android-base/scopeguard.h> +#include <android-base/stringprintf.h> +#include <log/log_event_list.h> +#include <log/log_properties.h> +#include <private/android_filesystem_config.h> + +#include "LogTags.h" +#include "LogUtils.h" + +using android::base::make_scope_guard; + +static LogTags* logtags; + +const char LogTags::system_event_log_tags[] = "/system/etc/event-log-tags"; +const char LogTags::dynamic_event_log_tags[] = "/dev/event-log-tags"; +// Only for debug +const char LogTags::debug_event_log_tags[] = "/data/misc/logd/event-log-tags"; + +// Sniff for first uid=%d in utf8z comment string +static uid_t sniffUid(const char* comment, const char* endp) { + if (!comment) return AID_ROOT; + + if (*comment == '#') ++comment; + while ((comment < endp) && (*comment != '\n') && isspace(*comment)) + ++comment; + static const char uid_str[] = "uid="; + if (((comment + strlen(uid_str)) >= endp) || + fastcmp<strncmp>(comment, uid_str, strlen(uid_str)) || + !isdigit(comment[strlen(uid_str)])) + return AID_ROOT; + char* cp; + unsigned long Uid = strtoul(comment + 4, &cp, 10); + if ((cp > endp) || (Uid >= INT_MAX)) return AID_ROOT; + + return Uid; +} + +// Checks for file corruption, and report false if there was no need +// to rebuild the referenced file. Failure to rebuild is only logged, +// does not cause a return value of false. +bool LogTags::RebuildFileEventLogTags(const char* filename, bool warn) { + int fd; + + { + android::RWLock::AutoRLock readLock(rwlock); + + if (tag2total.begin() == tag2total.end()) { + return false; + } + + file2watermark_const_iterator iwater = file2watermark.find(filename); + if (iwater == file2watermark.end()) { + return false; + } + + struct stat sb; + if (!stat(filename, &sb) && ((size_t)sb.st_size >= iwater->second)) { + return false; + } + + // dump what we already know back into the file? + fd = TEMP_FAILURE_RETRY(open( + filename, O_WRONLY | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY)); + if (fd >= 0) { + time_t now = time(nullptr); + struct tm tm; + localtime_r(&now, &tm); + char timebuf[20]; + size_t len = + strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", &tm); + android::base::WriteStringToFd( + android::base::StringPrintf( + "# Rebuilt %.20s, content owned by logd\n", timebuf), + fd); + for (const auto& it : tag2total) { + android::base::WriteStringToFd( + formatEntry_locked(it.first, AID_ROOT), fd); + } + close(fd); + } + } + + if (warn) { + android::prdebug( + ((fd < 0) ? "%s failed to rebuild" + : "%s missing, damaged or truncated; rebuilt"), + filename); + } + + if (fd >= 0) { + android::RWLock::AutoWLock writeLock(rwlock); + + struct stat sb; + if (!stat(filename, &sb)) file2watermark[filename] = sb.st_size; + } + + return true; +} + +void LogTags::AddEventLogTags(uint32_t tag, uid_t uid, const std::string& Name, + const std::string& Format, const char* source, + bool warn) { + std::string Key = Name; + if (Format.length()) Key += "+" + Format; + + bool update = !source || !!strcmp(source, system_event_log_tags); + bool newOne; + + { + android::RWLock::AutoWLock writeLock(rwlock); + + tag2total_const_iterator itot = tag2total.find(tag); + + // unlikely except for dupes, or updates to uid list (more later) + if (itot != tag2total.end()) update = false; + + newOne = tag2name.find(tag) == tag2name.end(); + key2tag[Key] = tag; + + if (Format.length()) { + if (key2tag.find(Name) == key2tag.end()) { + key2tag[Name] = tag; + } + tag2format[tag] = Format; + } + tag2name[tag] = Name; + + tag2uid_const_iterator ut = tag2uid.find(tag); + if (ut != tag2uid.end()) { + if (uid == AID_ROOT) { + tag2uid.erase(ut); + update = true; + } else if (ut->second.find(uid) == ut->second.end()) { + const_cast<uid_list&>(ut->second).emplace(uid); + update = true; + } + } else if (newOne && (uid != AID_ROOT)) { + tag2uid[tag].emplace(uid); + update = true; + } + + // updatePersist -> trigger output on modified + // content, reset tag2total if available + if (update && (itot != tag2total.end())) tag2total[tag] = 0; + } + + if (update) { + WritePersistEventLogTags(tag, uid, source); + } else if (warn && !newOne && source) { + // For the files, we want to report dupes. + android::prdebug("Multiple tag %" PRIu32 " %s %s %s", tag, Name.c_str(), + Format.c_str(), source); + } +} + +// Read the event log tags file, and build up our internal database +void LogTags::ReadFileEventLogTags(const char* filename, bool warn) { + bool etc = !strcmp(filename, system_event_log_tags); + bool debug = !etc && !strcmp(filename, debug_event_log_tags); + + if (!etc) { + RebuildFileEventLogTags(filename, warn); + } + std::string content; + if (android::base::ReadFileToString(filename, &content)) { + char* cp = (char*)content.c_str(); + char* endp = cp + content.length(); + + { + android::RWLock::AutoRLock writeLock(rwlock); + + file2watermark[filename] = content.length(); + } + + char* lineStart = cp; + while (cp < endp) { + if (*cp == '\n') { + lineStart = cp; + } else if (lineStart) { + if (*cp == '#') { + /* comment; just scan to end */ + lineStart = nullptr; + } else if (isdigit(*cp)) { + unsigned long Tag = strtoul(cp, &cp, 10); + if (warn && (Tag > emptyTag)) { + android::prdebug("tag too large %lu", Tag); + } + while ((cp < endp) && (*cp != '\n') && isspace(*cp)) ++cp; + if (cp >= endp) break; + if (*cp == '\n') continue; + const char* name = cp; + /* Determine whether it is a valid tag name [a-zA-Z0-9_] */ + bool hasAlpha = false; + while ((cp < endp) && (isalnum(*cp) || (*cp == '_'))) { + if (!isdigit(*cp)) hasAlpha = true; + ++cp; + } + std::string Name(name, cp - name); +#ifdef ALLOW_NOISY_LOGGING_OF_PROBLEM_WITH_LOTS_OF_TECHNICAL_DEBT + static const size_t maximum_official_tag_name_size = 24; + if (warn && + (Name.length() > maximum_official_tag_name_size)) { + android::prdebug("tag name too long %s", Name.c_str()); + } +#endif + if (hasAlpha && + ((cp >= endp) || (*cp == '#') || isspace(*cp))) { + if (Tag > emptyTag) { + if (*cp != '\n') lineStart = nullptr; + continue; + } + while ((cp < endp) && (*cp != '\n') && isspace(*cp)) + ++cp; + const char* format = cp; + uid_t uid = AID_ROOT; + while ((cp < endp) && (*cp != '\n')) { + if (*cp == '#') { + uid = sniffUid(cp, endp); + lineStart = nullptr; + break; + } + ++cp; + } + while ((cp > format) && isspace(cp[-1])) { + --cp; + lineStart = nullptr; + } + std::string Format(format, cp - format); + + AddEventLogTags((uint32_t)Tag, uid, Name, Format, + filename, warn); + } else { + if (warn) { + android::prdebug("tag name invalid %.*s", + (int)(cp - name + 1), name); + } + lineStart = nullptr; + } + } else if (!isspace(*cp)) { + break; + } + } + cp++; + } + } else if (warn) { + android::prdebug("Cannot read %s", filename); + } +} + +// Extract a 4-byte value from a byte stream. +static inline uint32_t get4LE(const char* msg) { + const uint8_t* src = reinterpret_cast<const uint8_t*>(msg); + return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); +} + +// Additional persistent sources for invented log tags. Read the +// special pmsg event for log tags, and build up our internal +// database with any found. +void LogTags::ReadPersistEventLogTags() { + struct logger_list* logger_list = android_logger_list_alloc( + ANDROID_LOG_RDONLY | ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK, 0, + (pid_t)0); + if (!logger_list) return; + + struct logger* e = android_logger_open(logger_list, LOG_ID_EVENTS); + struct logger* s = android_logger_open(logger_list, LOG_ID_SECURITY); + if (!e && !s) { + android_logger_list_free(logger_list); + return; + } + + for (;;) { + struct log_msg log_msg; + int ret = android_logger_list_read(logger_list, &log_msg); + if (ret <= 0) break; + + const char* msg = log_msg.msg(); + if (!msg) continue; + if (log_msg.entry.len <= sizeof(uint32_t)) continue; + uint32_t Tag = get4LE(msg); + if (Tag != TAG_DEF_LOG_TAG) continue; + uid_t uid = log_msg.entry.uid; + + std::string Name; + std::string Format; + android_log_list_element elem; + { + auto ctx = create_android_log_parser(log_msg.msg() + sizeof(uint32_t), + log_msg.entry.len - sizeof(uint32_t)); + auto guard = make_scope_guard([&ctx]() { android_log_destroy(&ctx); }); + elem = android_log_read_next(ctx); + if (elem.type != EVENT_TYPE_LIST) { + continue; + } + elem = android_log_read_next(ctx); + if (elem.type != EVENT_TYPE_INT) { + continue; + } + Tag = elem.data.int32; + elem = android_log_read_next(ctx); + if (elem.type != EVENT_TYPE_STRING) { + continue; + } + Name = std::string(elem.data.string, elem.len); + elem = android_log_read_next(ctx); + if (elem.type != EVENT_TYPE_STRING) { + continue; + } + Format = std::string(elem.data.string, elem.len); + elem = android_log_read_next(ctx); + } + if ((elem.type != EVENT_TYPE_LIST_STOP) || !elem.complete) continue; + + AddEventLogTags(Tag, uid, Name, Format); + } + android_logger_list_free(logger_list); +} + +LogTags::LogTags() { + ReadFileEventLogTags(system_event_log_tags); + // Following will likely fail on boot, but is required if logd restarts + ReadFileEventLogTags(dynamic_event_log_tags, false); + if (__android_log_is_debuggable()) { + ReadFileEventLogTags(debug_event_log_tags, false); + } + ReadPersistEventLogTags(); + + logtags = this; +} + +// Converts an event tag into a name +const char* LogTags::tagToName(uint32_t tag) const { + tag2name_const_iterator it; + + android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock)); + + it = tag2name.find(tag); + if ((it == tag2name.end()) || (it->second.length() == 0)) return nullptr; + + return it->second.c_str(); +} + +// Prototype in LogUtils.h allowing external access to our database. +// +// This must be a pure reader to our database, as everything else is +// guaranteed single-threaded except this access point which is +// asynchonous and can be multithreaded and thus rentrant. The +// object's rwlock is only used to guarantee atomic access to the +// unordered_map to prevent corruption, with a requirement to be a +// low chance of contention for this call. If we end up changing +// this algorithm resulting in write, then we should use a different +// lock than the object's rwlock to protect groups of associated +// actions. +const char* android::tagToName(uint32_t tag) { + LogTags* me = logtags; + + if (!me) return nullptr; + me->WritePmsgEventLogTags(tag); + return me->tagToName(tag); +} + +// Prototype in LogUtils.h allowing external access to our database. +// +// This only works on userdebug and eng devices to re-read the +// /data/misc/logd/event-log-tags file right after /data is mounted. +// The operation is near to boot and should only happen once. There +// are races associated with its use since it can trigger a Rebuild +// of the file, but that is a can-not-happen since the file was not +// read yet. More dangerous if called later, but if all is well it +// should just skip over everything and not write any new entries. +void android::ReReadEventLogTags() { + LogTags* me = logtags; + + if (me && __android_log_is_debuggable()) { + me->ReadFileEventLogTags(me->debug_event_log_tags); + } +} + +// converts an event tag into a format +const char* LogTags::tagToFormat(uint32_t tag) const { + tag2format_const_iterator iform; + + android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock)); + + iform = tag2format.find(tag); + if (iform == tag2format.end()) return nullptr; + + return iform->second.c_str(); +} + +// converts a name into an event tag +uint32_t LogTags::nameToTag(const char* name) const { + uint32_t ret = emptyTag; + + // Bug: Only works for a single entry, we can have multiple entries, + // one for each format, so we find first entry recorded, or entry with + // no format associated with it. + + android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock)); + + key2tag_const_iterator ik = key2tag.find(std::string(name)); + if (ik != key2tag.end()) ret = ik->second; + + return ret; +} + +// Caller must perform locks, can be under reader (for pre-check) or +// writer lock. We use this call to invent a new deterministically +// random tag, unique is cleared if no conflicts. If format is NULL, +// we are in readonly mode. +uint32_t LogTags::nameToTag_locked(const std::string& name, const char* format, + bool& unique) { + key2tag_const_iterator ik; + + bool write = format != nullptr; + unique = write; + + if (!write) { + // Bug: Only works for a single entry, we can have multiple entries, + // one for each format, so we find first entry recorded, or entry with + // no format associated with it. + ik = key2tag.find(name); + if (ik == key2tag.end()) return emptyTag; + return ik->second; + } + + std::string Key(name); + if (*format) Key += std::string("+") + format; + + ik = key2tag.find(Key); + if (ik != key2tag.end()) { + unique = false; + return ik->second; + } + + size_t Hash = key2tag.hash_function()(Key); + uint32_t Tag = Hash; + // This sets an upper limit on the conflics we are allowed to deal with. + for (unsigned i = 0; i < 256;) { + tag2name_const_iterator it = tag2name.find(Tag); + if (it == tag2name.end()) return Tag; + std::string localKey(it->second); + tag2format_const_iterator iform = tag2format.find(Tag); + if ((iform == tag2format.end()) && iform->second.length()) { + localKey += "+" + iform->second; + } + unique = !!it->second.compare(localKey); + if (!unique) return Tag; // unlikely except in a race + + ++i; + // Algorithm to convert hash to next tag + if (i < 32) { + Tag = (Hash >> i); + // size_t is 32 bits, or upper word zero, rotate + if ((sizeof(Hash) <= 4) || ((Hash & (uint64_t(-1LL) << 32)) == 0)) { + Tag |= Hash << (32 - i); + } + } else { + Tag = Hash + i - 31; + } + } + return emptyTag; +} + +static int openFile(const char* name, int mode, bool warning) { + int fd = TEMP_FAILURE_RETRY(open(name, mode)); + if ((fd < 0) && warning) { + android::prdebug("Failed open %s (%d)", name, errno); + } + return fd; +} + +void LogTags::WritePmsgEventLogTags(uint32_t tag, uid_t uid) { + android::RWLock::AutoRLock readLock(rwlock); + + tag2total_const_iterator itot = tag2total.find(tag); + if (itot == tag2total.end()) return; // source is a static entry + + size_t lastTotal = itot->second; + + // Every 16K (half the smallest configurable pmsg buffer size) record + static const size_t rate_to_pmsg = 16 * 1024; + if (lastTotal && ((android::sizesTotal() - lastTotal) < rate_to_pmsg)) { + return; + } + + static int pmsg_fd = -1; + if (pmsg_fd < 0) { + pmsg_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC)); + // unlikely, but deal with partners with borken pmsg + if (pmsg_fd < 0) return; + } + + std::string Name = tag2name[tag]; + tag2format_const_iterator iform = tag2format.find(tag); + std::string Format = (iform != tag2format.end()) ? iform->second : ""; + + auto ctx = create_android_logger(TAG_DEF_LOG_TAG); + auto guard = make_scope_guard([&ctx]() { android_log_destroy(&ctx); }); + if (android_log_write_int32(ctx, static_cast<int32_t>(tag) < 0) || + android_log_write_string8_len(ctx, Name.c_str(), Name.size()) < 0 || + android_log_write_string8_len(ctx, Format.c_str(), Format.size()) < 0) { + return; + } + + const char* cp = nullptr; + ssize_t len = android_log_write_list_buffer(ctx, &cp); + + if (len <= 0 || cp == nullptr) { + return; + } + + std::string buffer(cp, len); + + /* + * 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; + * }; + * }; + */ + + struct timespec ts; + clock_gettime(android_log_clockid(), &ts); + + android_log_header_t header = { + .id = LOG_ID_EVENTS, + .tid = (uint16_t)gettid(), + .realtime.tv_sec = (uint32_t)ts.tv_sec, + .realtime.tv_nsec = (uint32_t)ts.tv_nsec, + }; + + uint32_t outTag = TAG_DEF_LOG_TAG; + outTag = get4LE((const char*)&outTag); + + android_pmsg_log_header_t pmsgHeader = { + .magic = LOGGER_MAGIC, + .len = (uint16_t)(sizeof(pmsgHeader) + sizeof(header) + sizeof(outTag) + + buffer.length()), + .uid = (uint16_t)AID_ROOT, + .pid = (uint16_t)getpid(), + }; + + struct iovec Vec[] = { { (unsigned char*)&pmsgHeader, sizeof(pmsgHeader) }, + { (unsigned char*)&header, sizeof(header) }, + { (unsigned char*)&outTag, sizeof(outTag) }, + { (unsigned char*)const_cast<char*>(buffer.data()), + buffer.length() } }; + + tag2uid_const_iterator ut = tag2uid.find(tag); + if (ut == tag2uid.end()) { + TEMP_FAILURE_RETRY(writev(pmsg_fd, Vec, arraysize(Vec))); + } else if (uid != AID_ROOT) { + pmsgHeader.uid = (uint16_t)uid; + TEMP_FAILURE_RETRY(writev(pmsg_fd, Vec, arraysize(Vec))); + } else { + for (auto& it : ut->second) { + pmsgHeader.uid = (uint16_t)it; + TEMP_FAILURE_RETRY(writev(pmsg_fd, Vec, arraysize(Vec))); + } + } +} + +void LogTags::WriteDynamicEventLogTags(uint32_t tag, uid_t uid) { + static const int mode = + O_WRONLY | O_APPEND | O_CLOEXEC | O_NOFOLLOW | O_BINARY; + + int fd = openFile(dynamic_event_log_tags, mode, true); + if (fd < 0) return; + + android::RWLock::AutoWLock writeLock(rwlock); + + std::string ret = formatEntry_locked(tag, uid, false); + android::base::WriteStringToFd(ret, fd); + close(fd); + + size_t size = 0; + file2watermark_const_iterator iwater; + + iwater = file2watermark.find(dynamic_event_log_tags); + if (iwater != file2watermark.end()) size = iwater->second; + + file2watermark[dynamic_event_log_tags] = size + ret.length(); +} + +void LogTags::WriteDebugEventLogTags(uint32_t tag, uid_t uid) { + static const int mode = + O_WRONLY | O_APPEND | O_CLOEXEC | O_NOFOLLOW | O_BINARY; + + static bool one = true; + int fd = openFile(debug_event_log_tags, mode, one); + one = fd >= 0; + if (!one) return; + + android::RWLock::AutoWLock writeLock(rwlock); + + std::string ret = formatEntry_locked(tag, uid, false); + android::base::WriteStringToFd(ret, fd); + close(fd); + + size_t size = 0; + file2watermark_const_iterator iwater; + + iwater = file2watermark.find(debug_event_log_tags); + if (iwater != file2watermark.end()) size = iwater->second; + + file2watermark[debug_event_log_tags] = size + ret.length(); +} + +// How we maintain some runtime or reboot stickiness +void LogTags::WritePersistEventLogTags(uint32_t tag, uid_t uid, + const char* source) { + // very unlikely + bool etc = source && !strcmp(source, system_event_log_tags); + if (etc) return; + + bool dynamic = source && !strcmp(source, dynamic_event_log_tags); + bool debug = (!dynamic && source && !strcmp(source, debug_event_log_tags)) || + !__android_log_is_debuggable(); + + WritePmsgEventLogTags(tag, uid); + + size_t lastTotal = 0; + { + android::RWLock::AutoRLock readLock(rwlock); + + tag2total_const_iterator itot = tag2total.find(tag); + if (itot != tag2total.end()) lastTotal = itot->second; + } + + if (lastTotal == 0) { // denotes first time for this one + if (!dynamic || !RebuildFileEventLogTags(dynamic_event_log_tags)) { + WriteDynamicEventLogTags(tag, uid); + } + + if (!debug && !RebuildFileEventLogTags(debug_event_log_tags)) { + WriteDebugEventLogTags(tag, uid); + } + } + + lastTotal = android::sizesTotal(); + if (!lastTotal) ++lastTotal; + + // record totals for next watermark. + android::RWLock::AutoWLock writeLock(rwlock); + tag2total[tag] = lastTotal; +} + +// nameToTag converts a name into an event tag. If format is NULL, then we +// are in readonly mode. +uint32_t LogTags::nameToTag(uid_t uid, const char* name, const char* format) { + std::string Name = std::string(name); + bool write = format != nullptr; + bool updateUid = uid != AID_ROOT; + bool updateFormat = format && *format; + bool unique; + uint32_t Tag; + + { + android::RWLock::AutoRLock readLock(rwlock); + + Tag = nameToTag_locked(Name, format, unique); + if (updateUid && (Tag != emptyTag) && !unique) { + tag2uid_const_iterator ut = tag2uid.find(Tag); + if (updateUid) { + if ((ut != tag2uid.end()) && + (ut->second.find(uid) == ut->second.end())) { + unique = write; // write passthrough to update uid counts + if (!write) Tag = emptyTag; // deny read access + } + } else { + unique = write && (ut != tag2uid.end()); + } + } + } + + if (Tag == emptyTag) return Tag; + WritePmsgEventLogTags(Tag, uid); // record references periodically + if (!unique) return Tag; + + bool updateWrite = false; + bool updateTag; + + // Special case of AddEventLogTags, checks per-uid counter which makes + // no sense there, and is also optimized somewhat to reduce write times. + { + android::RWLock::AutoWLock writeLock(rwlock); + + // double check after switch from read lock to write lock for Tag + updateTag = tag2name.find(Tag) == tag2name.end(); + // unlikely, either update, race inviting conflict or multiple uids + if (!updateTag) { + Tag = nameToTag_locked(Name, format, unique); + if (Tag == emptyTag) return Tag; + // is it multiple uid's setting this value + if (!unique) { + tag2uid_const_iterator ut = tag2uid.find(Tag); + if (updateUid) { + // Add it to the uid list + if ((ut == tag2uid.end()) || + (ut->second.find(uid) != ut->second.end())) { + return Tag; + } + const_cast<uid_list&>(ut->second).emplace(uid); + updateWrite = true; + } else { + if (ut == tag2uid.end()) return Tag; + // (system) adding a global one, erase the uid list + tag2uid.erase(ut); + updateWrite = true; + } + } + } + + // Update section + size_t count; + if (updateUid) { + count = 0; + uid2count_const_iterator ci = uid2count.find(uid); + if (ci != uid2count.end()) { + count = ci->second; + if (count >= max_per_uid) { + if (!updateWrite) return emptyTag; + // If we are added to the per-Uid perms, leak the Tag + // if it already exists. + updateUid = false; + updateTag = false; + updateFormat = false; + } + } + } + + // updateWrite -> trigger output on modified content, reset tag2total + // also sets static to dynamic entries if they are alterred, + // only occurs if they have a uid, and runtime adds another uid. + if (updateWrite) tag2total[Tag] = 0; + + if (updateTag) { + // mark as a dynamic entry, but do not upset current total counter + tag2total_const_iterator itot = tag2total.find(Tag); + if (itot == tag2total.end()) tag2total[Tag] = 0; + + if (*format) { + key2tag[Name + "+" + format] = Tag; + if (key2tag.find(Name) == key2tag.end()) key2tag[Name] = Tag; + } else { + key2tag[Name] = Tag; + } + tag2name[Tag] = Name; + } + if (updateFormat) tag2format[Tag] = format; + + if (updateUid) { + tag2uid[Tag].emplace(uid); + uid2count[uid] = count + 1; + } + } + + if (updateTag || updateFormat || updateWrite) { + WritePersistEventLogTags(Tag, uid); + } + + return Tag; +} + +std::string LogTags::formatEntry(uint32_t tag, uid_t uid, const char* name, + const char* format) { + if (!format || !format[0]) { + return android::base::StringPrintf("%" PRIu32 "\t%s\n", tag, name); + } + size_t len = (strlen(name) + 7) / 8; + static const char tabs[] = "\t\t\t"; + if (len > strlen(tabs)) len = strlen(tabs); + std::string Uid; + if (uid != AID_ROOT) Uid = android::base::StringPrintf(" # uid=%u", uid); + return android::base::StringPrintf("%" PRIu32 "\t%s%s\t%s%s\n", tag, name, + &tabs[len], format, Uid.c_str()); +} + +std::string LogTags::formatEntry_locked(uint32_t tag, uid_t uid, + bool authenticate) { + const char* name = tag2name[tag].c_str(); + + const char* format = ""; + tag2format_const_iterator iform = tag2format.find(tag); + if (iform != tag2format.end()) format = iform->second.c_str(); + + // Access permission test, do not report dynamic entries + // that do not belong to us. + tag2uid_const_iterator ut = tag2uid.find(tag); + if (ut == tag2uid.end()) { + return formatEntry(tag, AID_ROOT, name, format); + } + if (uid != AID_ROOT) { + if (authenticate && (ut->second.find(uid) == ut->second.end())) { + return std::string(""); + } + return formatEntry(tag, uid, name, format); + } + + // Show all, one for each registered uid (we are group root) + std::string ret; + for (auto& it : ut->second) { + ret += formatEntry(tag, it, name, format); + } + return ret; +} + +std::string LogTags::formatEntry(uint32_t tag, uid_t uid) { + android::RWLock::AutoRLock readLock(rwlock); + return formatEntry_locked(tag, uid); +} + +std::string LogTags::formatGetEventTag(uid_t uid, const char* name, + const char* format) { + bool all = name && (name[0] == '*') && !name[1]; + bool list = !name || all; + std::string ret; + + if (!list) { + // switch to read entry only if format == "*" + if (format && (format[0] == '*') && !format[1]) format = nullptr; + + // WAI: for null format, only works for a single entry, we can have + // multiple entries, one for each format, so we find first entry + // recorded, or entry with no format associated with it. + // We may desire to print all that match the name, but we did not + // add a mapping table for that and the cost is too high. + uint32_t tag = nameToTag(uid, name, format); + if (tag == emptyTag) return std::string("-1 ESRCH"); + if (uid == AID_ROOT) { + android::RWLock::AutoRLock readLock(rwlock); + + // first uid in list so as to manufacture an accurate reference + tag2uid_const_iterator ut = tag2uid.find(tag); + if ((ut != tag2uid.end()) && + (ut->second.begin() != ut->second.end())) { + uid = *(ut->second.begin()); + } + } + ret = formatEntry(tag, uid, name, format ?: tagToFormat(tag)); + if (!ret.length()) return std::string("-1 ESRCH"); + return ret; + } + + android::RWLock::AutoRLock readLock(rwlock); + if (all) { + // everything under the sun + for (const auto& it : tag2name) { + ret += formatEntry_locked(it.first, uid); + } + } else { + // set entries are dynamic + for (const auto& it : tag2total) { + ret += formatEntry_locked(it.first, uid); + } + } + return ret; +} |