/* * 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 "berberis/base/tracing.h" #include #include #include #include #include #include #include "berberis/base/checks.h" #include "berberis/base/config_globals.h" #include "berberis/base/file.h" #include "berberis/base/logging.h" #include "berberis/base/scoped_errno.h" namespace berberis { namespace { int TraceToFile(std::string trace_filename) { if (trace_filename == "1") { ALOGD("tracing to stdout"); return STDOUT_FILENO; } if (trace_filename == "2") { ALOGD("tracing to stderr"); return STDERR_FILENO; } // If the provided path is relative set it up under app's private directory. if (trace_filename.at(0) != '/') { const char* app_private_dir = GetAppPrivateDir(); if (!app_private_dir) { ALOGE("not tracing - app's private directory is undefined"); return -1; } trace_filename = std::string(app_private_dir) + "/" + trace_filename; } if (uid_t uid = getuid()) { // If not running as root, should be output file directory owner. // To trace an app, use output file in app's data directory. std::string dir = Dirname(trace_filename); struct stat dir_stat; if (stat(dir.c_str(), &dir_stat)) { ALOGE("not tracing - failed to stat \"%s\"", dir.c_str()); return -1; } if (uid != dir_stat.st_uid) { ALOGE("not tracing - uid mismatch of \"%s\"", dir.c_str()); return -1; } } int fd = open(trace_filename.c_str(), O_WRONLY | O_CREAT | O_APPEND | O_CLOEXEC, S_IWUSR); if (fd == -1) { ALOGE("not tracing - failed to open output file \"%s\"", trace_filename.c_str()); return -1; } ALOGD("tracing to \"%s\"", trace_filename.c_str()); return fd; } // At the moment, only accept ":", assume localhost: int TraceToSocket(const char* env) { CHECK_EQ(':', env[0]); struct addrinfo hints {}; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; struct addrinfo* results; if (getaddrinfo("localhost", env + 1, &hints, &results) != 0) { ALOGE("not tracing - failed to get info for \"%s\"", env); return -1; } for (auto info = results; info; info = info->ai_next) { int fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol); if (fd != -1) { if (connect(fd, info->ai_addr, info->ai_addrlen) != -1) { freeaddrinfo(results); ALOGD("tracing to \"localhost%s\"", env); return fd; } close(fd); } } freeaddrinfo(results); ALOGE("not tracing - failed to connect to \"%s\"", env); return -1; } } // namespace int Tracing::fd_ = -1; void Tracing::InitImpl() { ScopedErrno scoped_errno; auto env = GetTracingConfig(); if (!env || !env[0]) { return; } if (const char* filter_end = strchr(env, '=')) { const char* app = GetAppPackageName(); if (!app || strncmp(app, env, filter_end - env) != 0) { ALOGD("not tracing - filtered out"); return; } env = filter_end + 1; } if (env[0] == ':') { fd_ = TraceToSocket(env); } else { fd_ = TraceToFile(env); } } } // namespace berberis