blob: d5aac6df3c576901a699957326c1b7d138175afa [file] [log] [blame]
Yifan Hong538d8522020-02-25 17:13:53 -08001//
2// Copyright (C) 2020 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#include <inttypes.h>
18#include <stdio.h>
19#include <sys/stat.h>
20#include <unistd.h>
21
22#include <algorithm>
Yifan Hongf9666d72020-03-02 13:03:25 -080023#include <functional>
24#include <iomanip>
Yifan Hong538d8522020-02-25 17:13:53 -080025#include <string>
Yifan Hongf9666d72020-03-02 13:03:25 -080026#include <string_view>
Yifan Hong538d8522020-02-25 17:13:53 -080027#include <vector>
28
Yifan Hongf9666d72020-03-02 13:03:25 -080029#include <android-base/file.h>
30#include <android-base/strings.h>
31#include <android-base/unique_fd.h>
Yifan Hong538d8522020-02-25 17:13:53 -080032#include <base/files/dir_reader_posix.h>
33#include <base/logging.h>
34#include <base/strings/string_util.h>
35#include <base/strings/stringprintf.h>
Yifan Hongf9666d72020-03-02 13:03:25 -080036#include <log/log.h>
Yifan Hong538d8522020-02-25 17:13:53 -080037
38#include "update_engine/common/utils.h"
39
40using std::string;
41
42namespace chromeos_update_engine {
43namespace {
44
45constexpr char kSystemLogsRoot[] = "/data/misc/update_engine_log";
46constexpr size_t kLogCount = 5;
47
48// Keep the most recent |kLogCount| logs but remove the old ones in
49// "/data/misc/update_engine_log/".
50void DeleteOldLogs(const string& kLogsRoot) {
51 base::DirReaderPosix reader(kLogsRoot.c_str());
52 if (!reader.IsValid()) {
53 LOG(ERROR) << "Failed to read " << kLogsRoot;
54 return;
55 }
56
57 std::vector<string> old_logs;
58 while (reader.Next()) {
59 if (reader.name()[0] == '.')
60 continue;
61
62 // Log files are in format "update_engine.%Y%m%d-%H%M%S",
63 // e.g. update_engine.20090103-231425
64 uint64_t date;
65 uint64_t local_time;
66 if (sscanf(reader.name(),
67 "update_engine.%" PRIu64 "-%" PRIu64 "",
68 &date,
69 &local_time) == 2) {
70 old_logs.push_back(reader.name());
71 } else {
72 LOG(WARNING) << "Unrecognized log file " << reader.name();
73 }
74 }
75
76 std::sort(old_logs.begin(), old_logs.end(), std::greater<string>());
77 for (size_t i = kLogCount; i < old_logs.size(); i++) {
78 string log_path = kLogsRoot + "/" + old_logs[i];
79 if (unlink(log_path.c_str()) == -1) {
80 PLOG(WARNING) << "Failed to unlink " << log_path;
81 }
82 }
83}
84
85string SetupLogFile(const string& kLogsRoot) {
86 DeleteOldLogs(kLogsRoot);
87
88 return base::StringPrintf("%s/update_engine.%s",
89 kLogsRoot.c_str(),
90 utils::GetTimeAsString(::time(nullptr)).c_str());
91}
92
Yifan Hongf9666d72020-03-02 13:03:25 -080093const char* LogPriorityToCString(int priority) {
94 switch (priority) {
95 case ANDROID_LOG_VERBOSE:
96 return "VERBOSE";
97 case ANDROID_LOG_DEBUG:
98 return "DEBUG";
99 case ANDROID_LOG_INFO:
100 return "INFO";
101 case ANDROID_LOG_WARN:
102 return "WARN";
103 case ANDROID_LOG_ERROR:
104 return "ERROR";
105 case ANDROID_LOG_FATAL:
106 return "FATAL";
107 default:
108 return "UNKNOWN";
109 }
110}
111
Tom Cherry24969662020-03-06 16:37:45 -0800112using LoggerFunction = std::function<void(const struct __android_log_message*)>;
Yifan Hongf9666d72020-03-02 13:03:25 -0800113
114class FileLogger {
115 public:
116 explicit FileLogger(const string& path) {
117 fd_.reset(TEMP_FAILURE_RETRY(
118 open(path.c_str(),
119 O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_SYNC,
120 0644)));
121 if (fd_ == -1) {
122 // Use ALOGE that logs to logd before __android_log_set_logger.
123 ALOGE("Cannot open persistent log %s: %s", path.c_str(), strerror(errno));
124 return;
125 }
126 // The log file will have AID_LOG as group ID; this GID is inherited from
127 // the parent directory "/data/misc/update_engine_log" which sets the SGID
128 // bit.
129 if (fchmod(fd_.get(), 0640) == -1) {
130 // Use ALOGE that logs to logd before __android_log_set_logger.
131 ALOGE("Cannot chmod 0640 persistent log %s: %s",
132 path.c_str(),
133 strerror(errno));
134 return;
135 }
136 }
137 // Copy-constructor needed to be converted to std::function.
138 FileLogger(const FileLogger& other) { fd_.reset(dup(other.fd_)); }
Tom Cherry24969662020-03-06 16:37:45 -0800139 void operator()(const struct __android_log_message* log_message) {
Yifan Hongf9666d72020-03-02 13:03:25 -0800140 if (fd_ == -1) {
141 return;
142 }
143
144 // libchrome add a newline character to |message|. Strip it.
Tom Cherry24969662020-03-06 16:37:45 -0800145 std::string_view message_no_newline =
146 log_message->message != nullptr ? log_message->message : "";
Yifan Hongf9666d72020-03-02 13:03:25 -0800147 ignore_result(android::base::ConsumeSuffix(&message_no_newline, "\n"));
148
Tom Cherry24969662020-03-06 16:37:45 -0800149 WriteToFd(GetPrefix(log_message));
Yifan Hongf9666d72020-03-02 13:03:25 -0800150 WriteToFd(message_no_newline);
151 WriteToFd("\n");
152 }
153
154 private:
155 android::base::unique_fd fd_;
156 void WriteToFd(std::string_view message) {
157 ignore_result(
158 android::base::WriteFully(fd_, message.data(), message.size()));
159 }
160
Tom Cherry24969662020-03-06 16:37:45 -0800161 string GetPrefix(const struct __android_log_message* log_message) {
Yifan Hongf9666d72020-03-02 13:03:25 -0800162 std::stringstream ss;
163 timeval tv;
164 gettimeofday(&tv, nullptr);
165 time_t t = tv.tv_sec;
166 struct tm local_time;
167 localtime_r(&t, &local_time);
168 struct tm* tm_time = &local_time;
169 ss << "[" << std::setfill('0') << std::setw(2) << 1 + tm_time->tm_mon
170 << std::setw(2) << tm_time->tm_mday << '/' << std::setw(2)
171 << tm_time->tm_hour << std::setw(2) << tm_time->tm_min << std::setw(2)
172 << tm_time->tm_sec << '.' << std::setw(6) << tv.tv_usec << "] ";
173 // libchrome logs prepends |message| with severity, file and line, but
174 // leave logger_data->file as nullptr.
175 // libbase / liblog logs doesn't. Hence, add them to match the style.
176 // For liblog logs that doesn't set logger_data->file, not printing the
177 // priority is acceptable.
Tom Cherry24969662020-03-06 16:37:45 -0800178 if (log_message->file) {
179 ss << "[" << LogPriorityToCString(log_message->priority) << ':'
180 << log_message->file << '(' << log_message->line << ")] ";
Yifan Hongf9666d72020-03-02 13:03:25 -0800181 }
182 return ss.str();
183 }
184};
185
186class CombinedLogger {
187 public:
188 CombinedLogger(bool log_to_system, bool log_to_file) {
189 if (log_to_system) {
190 loggers_.push_back(__android_log_logd_logger);
191 }
192 if (log_to_file) {
193 loggers_.push_back(std::move(FileLogger(SetupLogFile(kSystemLogsRoot))));
194 }
195 }
Tom Cherry24969662020-03-06 16:37:45 -0800196 void operator()(const struct __android_log_message* log_message) {
Yifan Hongf9666d72020-03-02 13:03:25 -0800197 for (auto&& logger : loggers_) {
Tom Cherry24969662020-03-06 16:37:45 -0800198 logger(log_message);
Yifan Hongf9666d72020-03-02 13:03:25 -0800199 }
200 }
201
202 private:
203 std::vector<LoggerFunction> loggers_;
204};
205
Yifan Hong538d8522020-02-25 17:13:53 -0800206} // namespace
207
208void SetupLogging(bool log_to_system, bool log_to_file) {
Yifan Hongf9666d72020-03-02 13:03:25 -0800209 // Note that libchrome logging uses liblog.
210 // By calling liblog's __android_log_set_logger function, all of libchrome
211 // (used by update_engine) / libbase / liblog (used by depended modules)
212 // logging eventually redirects to CombinedLogger.
213 static auto g_logger =
214 std::make_unique<CombinedLogger>(log_to_system, log_to_file);
Tom Cherry24969662020-03-06 16:37:45 -0800215 __android_log_set_logger([](const struct __android_log_message* log_message) {
216 (*g_logger)(log_message);
217 });
Yifan Hongf9666d72020-03-02 13:03:25 -0800218
219 // libchrome logging should not log to file.
Yifan Hong538d8522020-02-25 17:13:53 -0800220 logging::LoggingSettings log_settings;
221 log_settings.lock_log = logging::DONT_LOCK_LOG_FILE;
222 log_settings.logging_dest = static_cast<logging::LoggingDestination>(
Yifan Hongf9666d72020-03-02 13:03:25 -0800223 logging::LOG_TO_SYSTEM_DEBUG_LOG);
Yifan Hong538d8522020-02-25 17:13:53 -0800224 log_settings.log_file = nullptr;
Yifan Hong538d8522020-02-25 17:13:53 -0800225 logging::InitLogging(log_settings);
Yifan Hongf9666d72020-03-02 13:03:25 -0800226 logging::SetLogItems(false /* enable_process_id */,
227 false /* enable_thread_id */,
228 false /* enable_timestamp */,
229 false /* enable_tickcount */);
Yifan Hong538d8522020-02-25 17:13:53 -0800230}
231
232} // namespace chromeos_update_engine