284 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			284 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			C++
		
	
	
	
| //
 | |
| // Copyright (C) 2020 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 <inttypes.h>
 | |
| #include <stdio.h>
 | |
| #include <sys/stat.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| #include <algorithm>
 | |
| #include <functional>
 | |
| #include <iomanip>
 | |
| #include <string>
 | |
| #include <string_view>
 | |
| #include <vector>
 | |
| 
 | |
| #include <android-base/file.h>
 | |
| #include <android-base/strings.h>
 | |
| #include <android-base/unique_fd.h>
 | |
| #include <base/files/dir_reader_posix.h>
 | |
| #include <base/logging.h>
 | |
| #include <base/strings/string_util.h>
 | |
| #include <base/strings/stringprintf.h>
 | |
| #include <log/log.h>
 | |
| 
 | |
| #include "update_engine/common/utils.h"
 | |
| 
 | |
| using std::string;
 | |
| 
 | |
| #ifdef _UE_SIDELOAD
 | |
| constexpr bool kSideload = true;
 | |
| #else
 | |
| constexpr bool kSideload = false;
 | |
| #endif
 | |
| 
 | |
| namespace chromeos_update_engine {
 | |
| namespace {
 | |
| 
 | |
| constexpr char kSystemLogsRoot[] = "/data/misc/update_engine_log";
 | |
| constexpr size_t kLogCount = 5;
 | |
| 
 | |
| // Keep the most recent |kLogCount| logs but remove the old ones in
 | |
| // "/data/misc/update_engine_log/".
 | |
| void DeleteOldLogs(const string& kLogsRoot) {
 | |
|   base::DirReaderPosix reader(kLogsRoot.c_str());
 | |
|   if (!reader.IsValid()) {
 | |
|     LOG(ERROR) << "Failed to read " << kLogsRoot;
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   std::vector<string> old_logs;
 | |
|   while (reader.Next()) {
 | |
|     if (reader.name()[0] == '.')
 | |
|       continue;
 | |
| 
 | |
|     // Log files are in format "update_engine.%Y%m%d-%H%M%S",
 | |
|     // e.g. update_engine.20090103-231425
 | |
|     uint64_t date;
 | |
|     uint64_t local_time;
 | |
|     if (sscanf(reader.name(),
 | |
|                "update_engine.%" PRIu64 "-%" PRIu64 "",
 | |
|                &date,
 | |
|                &local_time) == 2) {
 | |
|       old_logs.push_back(reader.name());
 | |
|     } else {
 | |
|       LOG(WARNING) << "Unrecognized log file " << reader.name();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   std::sort(old_logs.begin(), old_logs.end(), std::greater<string>());
 | |
|   for (size_t i = kLogCount; i < old_logs.size(); i++) {
 | |
|     string log_path = kLogsRoot + "/" + old_logs[i];
 | |
|     if (unlink(log_path.c_str()) == -1) {
 | |
|       PLOG(WARNING) << "Failed to unlink " << log_path;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| string SetupLogFile(const string& kLogsRoot) {
 | |
|   DeleteOldLogs(kLogsRoot);
 | |
| 
 | |
|   return base::StringPrintf("%s/update_engine.%s",
 | |
|                             kLogsRoot.c_str(),
 | |
|                             utils::GetTimeAsString(::time(nullptr)).c_str());
 | |
| }
 | |
| 
 | |
| const char* LogPriorityToCString(int priority) {
 | |
|   switch (priority) {
 | |
|     case ANDROID_LOG_VERBOSE:
 | |
|       return "VERBOSE";
 | |
|     case ANDROID_LOG_DEBUG:
 | |
|       return "DEBUG";
 | |
|     case ANDROID_LOG_INFO:
 | |
|       return "INFO";
 | |
|     case ANDROID_LOG_WARN:
 | |
|       return "WARN";
 | |
|     case ANDROID_LOG_ERROR:
 | |
|       return "ERROR";
 | |
|     case ANDROID_LOG_FATAL:
 | |
|       return "FATAL";
 | |
|     default:
 | |
|       return "UNKNOWN";
 | |
|   }
 | |
| }
 | |
| 
 | |
| using LoggerFunction = std::function<void(const struct __android_log_message*)>;
 | |
| 
 | |
| class FileLogger {
 | |
|  public:
 | |
|   explicit FileLogger(const string& path) {
 | |
|     fd_.reset(TEMP_FAILURE_RETRY(
 | |
|         open(path.c_str(),
 | |
|              O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_SYNC,
 | |
|              0644)));
 | |
|     if (fd_ == -1) {
 | |
|       // Use ALOGE that logs to logd before __android_log_set_logger.
 | |
|       ALOGE("Cannot open persistent log %s: %s", path.c_str(), strerror(errno));
 | |
|       return;
 | |
|     }
 | |
|     // The log file will have AID_LOG as group ID; this GID is inherited from
 | |
|     // the parent directory "/data/misc/update_engine_log" which sets the SGID
 | |
|     // bit.
 | |
|     if (fchmod(fd_.get(), 0640) == -1) {
 | |
|       // Use ALOGE that logs to logd before __android_log_set_logger.
 | |
|       ALOGE("Cannot chmod 0640 persistent log %s: %s",
 | |
|             path.c_str(),
 | |
|             strerror(errno));
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
|   // Copy-constructor needed to be converted to std::function.
 | |
|   FileLogger(const FileLogger& other) { fd_.reset(dup(other.fd_)); }
 | |
|   void operator()(const struct __android_log_message* log_message) {
 | |
|     if (fd_ == -1) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     std::string_view message_str =
 | |
|         log_message->message != nullptr ? log_message->message : "";
 | |
| 
 | |
|     WriteToFd(GetPrefix(log_message));
 | |
|     WriteToFd(message_str);
 | |
|     WriteToFd("\n");
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   android::base::unique_fd fd_;
 | |
|   void WriteToFd(std::string_view message) {
 | |
|     ignore_result(
 | |
|         android::base::WriteFully(fd_, message.data(), message.size()));
 | |
|   }
 | |
| 
 | |
|   string GetPrefix(const struct __android_log_message* log_message) {
 | |
|     std::stringstream ss;
 | |
|     timeval tv;
 | |
|     gettimeofday(&tv, nullptr);
 | |
|     time_t t = tv.tv_sec;
 | |
|     struct tm local_time;
 | |
|     localtime_r(&t, &local_time);
 | |
|     struct tm* tm_time = &local_time;
 | |
|     ss << "[" << std::setfill('0') << std::setw(2) << 1 + tm_time->tm_mon
 | |
|        << std::setw(2) << tm_time->tm_mday << '/' << std::setw(2)
 | |
|        << tm_time->tm_hour << std::setw(2) << tm_time->tm_min << std::setw(2)
 | |
|        << tm_time->tm_sec << '.' << std::setw(6) << tv.tv_usec << "] ";
 | |
|     // libchrome logs prepends |message| with severity, file and line, but
 | |
|     // leave logger_data->file as nullptr.
 | |
|     // libbase / liblog logs doesn't. Hence, add them to match the style.
 | |
|     // For liblog logs that doesn't set logger_data->file, not printing the
 | |
|     // priority is acceptable.
 | |
|     if (log_message->file) {
 | |
|       ss << "[" << LogPriorityToCString(log_message->priority) << ':'
 | |
|          << log_message->file << '(' << log_message->line << ")] ";
 | |
|     }
 | |
|     return ss.str();
 | |
|   }
 | |
| };
 | |
| 
 | |
| class CombinedLogger {
 | |
|  public:
 | |
|   CombinedLogger(bool log_to_system, bool log_to_file) {
 | |
|     if (log_to_system) {
 | |
|       if (kSideload) {
 | |
|         // No logd in sideload. Use stdout.
 | |
|         // recovery has already redirected stdio properly.
 | |
|         loggers_.push_back(__android_log_stderr_logger);
 | |
|       } else {
 | |
|         loggers_.push_back(__android_log_logd_logger);
 | |
|       }
 | |
|     }
 | |
|     if (log_to_file) {
 | |
|       loggers_.push_back(std::move(FileLogger(SetupLogFile(kSystemLogsRoot))));
 | |
|     }
 | |
|   }
 | |
|   void operator()(const struct __android_log_message* log_message) {
 | |
|     for (auto&& logger : loggers_) {
 | |
|       logger(log_message);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   std::vector<LoggerFunction> loggers_;
 | |
| };
 | |
| 
 | |
| // Redirect all libchrome logs to liblog using our custom handler that does
 | |
| // not call __android_log_write and explicitly write to stderr at the same
 | |
| // time. The preset CombinedLogger already writes to stderr properly.
 | |
| bool RedirectToLiblog(int severity,
 | |
|                       const char* file,
 | |
|                       int line,
 | |
|                       size_t message_start,
 | |
|                       const std::string& str_newline) {
 | |
|   android_LogPriority priority =
 | |
|       (severity < 0) ? ANDROID_LOG_VERBOSE : ANDROID_LOG_UNKNOWN;
 | |
|   switch (severity) {
 | |
|     case logging::LOG_INFO:
 | |
|       priority = ANDROID_LOG_INFO;
 | |
|       break;
 | |
|     case logging::LOG_WARNING:
 | |
|       priority = ANDROID_LOG_WARN;
 | |
|       break;
 | |
|     case logging::LOG_ERROR:
 | |
|       priority = ANDROID_LOG_ERROR;
 | |
|       break;
 | |
|     case logging::LOG_FATAL:
 | |
|       priority = ANDROID_LOG_FATAL;
 | |
|       break;
 | |
|   }
 | |
|   std::string_view sv = str_newline;
 | |
|   ignore_result(android::base::ConsumeSuffix(&sv, "\n"));
 | |
|   std::string str(sv.data(), sv.size());
 | |
| 
 | |
|   if (priority == ANDROID_LOG_FATAL) {
 | |
|     // Abort the program for priority FATAL. __android_log_assert will log the
 | |
|     // message to stderr and CombinedLogger.
 | |
|     __android_log_assert(nullptr, nullptr, "%s", str.c_str());
 | |
|   } else {
 | |
|     // This will eventually be redirected to CombinedLogger.
 | |
|     // Use nullptr as tag so that liblog infers log tag from getprogname().
 | |
|     __android_log_write(priority, nullptr /* tag */, str.c_str());
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| }  // namespace
 | |
| 
 | |
| void SetupLogging(bool log_to_system, bool log_to_file) {
 | |
|   // Note that libchrome logging uses liblog.
 | |
|   // By calling liblog's __android_log_set_logger function, all of libchrome
 | |
|   // (used by update_engine) / libbase / liblog (used by depended modules)
 | |
|   // logging eventually redirects to CombinedLogger.
 | |
|   static auto g_logger =
 | |
|       std::make_unique<CombinedLogger>(log_to_system, log_to_file);
 | |
|   __android_log_set_logger([](const struct __android_log_message* log_message) {
 | |
|     (*g_logger)(log_message);
 | |
|   });
 | |
| 
 | |
|   // libchrome logging should not log to file.
 | |
|   logging::LoggingSettings log_settings;
 | |
|   log_settings.lock_log = logging::DONT_LOCK_LOG_FILE;
 | |
|   log_settings.logging_dest =
 | |
|       static_cast<logging::LoggingDestination>(logging::LOG_NONE);
 | |
|   log_settings.log_file = nullptr;
 | |
|   logging::InitLogging(log_settings);
 | |
|   logging::SetLogItems(false /* enable_process_id */,
 | |
|                        false /* enable_thread_id */,
 | |
|                        false /* enable_timestamp */,
 | |
|                        false /* enable_tickcount */);
 | |
|   logging::SetLogMessageHandler(&RedirectToLiblog);
 | |
| }
 | |
| 
 | |
| }  // namespace chromeos_update_engine
 |