blob: d1f10d605b411ba924c07b0dfb567547364b01fc [file] [log] [blame]
Yifan Hongc80de2d2020-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 Hongf1fba652020-03-02 13:03:25 -080023#include <functional>
24#include <iomanip>
Kelvin Zhang7f0429f2023-04-04 16:39:35 -070025#include <sstream>
Yifan Hongc80de2d2020-02-25 17:13:53 -080026#include <string>
Yifan Hongf1fba652020-03-02 13:03:25 -080027#include <string_view>
Yifan Hongc80de2d2020-02-25 17:13:53 -080028#include <vector>
29
Yifan Hongf1fba652020-03-02 13:03:25 -080030#include <android-base/file.h>
31#include <android-base/strings.h>
32#include <android-base/unique_fd.h>
Yifan Hongc80de2d2020-02-25 17:13:53 -080033#include <base/files/dir_reader_posix.h>
34#include <base/logging.h>
35#include <base/strings/string_util.h>
Kelvin Zhangb9a9aa22024-10-15 10:38:35 -070036#include <android-base/stringprintf.h>
Yifan Hongf1fba652020-03-02 13:03:25 -080037#include <log/log.h>
Yifan Hongc80de2d2020-02-25 17:13:53 -080038
Kelvin Zhang7f0429f2023-04-04 16:39:35 -070039#include "android/log.h"
Yifan Hongc80de2d2020-02-25 17:13:53 -080040#include "update_engine/common/utils.h"
41
42using std::string;
43
Yifan Honge7574202020-04-06 15:08:24 -070044#ifdef _UE_SIDELOAD
45constexpr bool kSideload = true;
46#else
47constexpr bool kSideload = false;
48#endif
49
Yifan Hongc80de2d2020-02-25 17:13:53 -080050namespace chromeos_update_engine {
51namespace {
52
53constexpr char kSystemLogsRoot[] = "/data/misc/update_engine_log";
54constexpr size_t kLogCount = 5;
55
56// Keep the most recent |kLogCount| logs but remove the old ones in
57// "/data/misc/update_engine_log/".
58void DeleteOldLogs(const string& kLogsRoot) {
59 base::DirReaderPosix reader(kLogsRoot.c_str());
60 if (!reader.IsValid()) {
61 LOG(ERROR) << "Failed to read " << kLogsRoot;
62 return;
63 }
64
65 std::vector<string> old_logs;
66 while (reader.Next()) {
67 if (reader.name()[0] == '.')
68 continue;
69
70 // Log files are in format "update_engine.%Y%m%d-%H%M%S",
71 // e.g. update_engine.20090103-231425
72 uint64_t date;
73 uint64_t local_time;
74 if (sscanf(reader.name(),
75 "update_engine.%" PRIu64 "-%" PRIu64 "",
76 &date,
77 &local_time) == 2) {
78 old_logs.push_back(reader.name());
79 } else {
80 LOG(WARNING) << "Unrecognized log file " << reader.name();
81 }
82 }
83
84 std::sort(old_logs.begin(), old_logs.end(), std::greater<string>());
85 for (size_t i = kLogCount; i < old_logs.size(); i++) {
86 string log_path = kLogsRoot + "/" + old_logs[i];
87 if (unlink(log_path.c_str()) == -1) {
88 PLOG(WARNING) << "Failed to unlink " << log_path;
89 }
90 }
91}
92
93string SetupLogFile(const string& kLogsRoot) {
94 DeleteOldLogs(kLogsRoot);
95
Kelvin Zhangb9a9aa22024-10-15 10:38:35 -070096 return android::base::StringPrintf(
97 "%s/update_engine.%s",
98 kLogsRoot.c_str(),
99 utils::GetTimeAsString(::time(nullptr)).c_str());
Yifan Hongc80de2d2020-02-25 17:13:53 -0800100}
101
Yifan Hongf1fba652020-03-02 13:03:25 -0800102const char* LogPriorityToCString(int priority) {
103 switch (priority) {
104 case ANDROID_LOG_VERBOSE:
105 return "VERBOSE";
106 case ANDROID_LOG_DEBUG:
107 return "DEBUG";
108 case ANDROID_LOG_INFO:
109 return "INFO";
110 case ANDROID_LOG_WARN:
111 return "WARN";
112 case ANDROID_LOG_ERROR:
113 return "ERROR";
114 case ANDROID_LOG_FATAL:
115 return "FATAL";
116 default:
117 return "UNKNOWN";
118 }
119}
120
Tom Cherry35c71f72020-03-06 16:37:45 -0800121using LoggerFunction = std::function<void(const struct __android_log_message*)>;
Yifan Hongf1fba652020-03-02 13:03:25 -0800122
123class FileLogger {
124 public:
125 explicit FileLogger(const string& path) {
126 fd_.reset(TEMP_FAILURE_RETRY(
127 open(path.c_str(),
Kelvin Zhangd2a298d2024-01-03 18:34:09 -0800128 O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
Yifan Hongf1fba652020-03-02 13:03:25 -0800129 0644)));
130 if (fd_ == -1) {
131 // Use ALOGE that logs to logd before __android_log_set_logger.
132 ALOGE("Cannot open persistent log %s: %s", path.c_str(), strerror(errno));
133 return;
134 }
135 // The log file will have AID_LOG as group ID; this GID is inherited from
136 // the parent directory "/data/misc/update_engine_log" which sets the SGID
137 // bit.
138 if (fchmod(fd_.get(), 0640) == -1) {
139 // Use ALOGE that logs to logd before __android_log_set_logger.
140 ALOGE("Cannot chmod 0640 persistent log %s: %s",
141 path.c_str(),
142 strerror(errno));
143 return;
144 }
145 }
146 // Copy-constructor needed to be converted to std::function.
147 FileLogger(const FileLogger& other) { fd_.reset(dup(other.fd_)); }
Tom Cherry35c71f72020-03-06 16:37:45 -0800148 void operator()(const struct __android_log_message* log_message) {
Yifan Hongf1fba652020-03-02 13:03:25 -0800149 if (fd_ == -1) {
150 return;
151 }
152
Yifan Honge7574202020-04-06 15:08:24 -0700153 std::string_view message_str =
Tom Cherry35c71f72020-03-06 16:37:45 -0800154 log_message->message != nullptr ? log_message->message : "";
Yifan Hongf1fba652020-03-02 13:03:25 -0800155
Tom Cherry35c71f72020-03-06 16:37:45 -0800156 WriteToFd(GetPrefix(log_message));
Yifan Honge7574202020-04-06 15:08:24 -0700157 WriteToFd(message_str);
Yifan Hongf1fba652020-03-02 13:03:25 -0800158 WriteToFd("\n");
Kelvin Zhangd2a298d2024-01-03 18:34:09 -0800159 fsync(fd_);
Yifan Hongf1fba652020-03-02 13:03:25 -0800160 }
161
162 private:
163 android::base::unique_fd fd_;
164 void WriteToFd(std::string_view message) {
165 ignore_result(
166 android::base::WriteFully(fd_, message.data(), message.size()));
167 }
168
Tom Cherry35c71f72020-03-06 16:37:45 -0800169 string GetPrefix(const struct __android_log_message* log_message) {
Yifan Hongf1fba652020-03-02 13:03:25 -0800170 std::stringstream ss;
171 timeval tv;
172 gettimeofday(&tv, nullptr);
173 time_t t = tv.tv_sec;
174 struct tm local_time;
175 localtime_r(&t, &local_time);
176 struct tm* tm_time = &local_time;
177 ss << "[" << std::setfill('0') << std::setw(2) << 1 + tm_time->tm_mon
178 << std::setw(2) << tm_time->tm_mday << '/' << std::setw(2)
179 << tm_time->tm_hour << std::setw(2) << tm_time->tm_min << std::setw(2)
180 << tm_time->tm_sec << '.' << std::setw(6) << tv.tv_usec << "] ";
181 // libchrome logs prepends |message| with severity, file and line, but
182 // leave logger_data->file as nullptr.
183 // libbase / liblog logs doesn't. Hence, add them to match the style.
184 // For liblog logs that doesn't set logger_data->file, not printing the
185 // priority is acceptable.
Tom Cherry35c71f72020-03-06 16:37:45 -0800186 if (log_message->file) {
187 ss << "[" << LogPriorityToCString(log_message->priority) << ':'
188 << log_message->file << '(' << log_message->line << ")] ";
Yifan Hongf1fba652020-03-02 13:03:25 -0800189 }
190 return ss.str();
191 }
192};
193
194class CombinedLogger {
195 public:
196 CombinedLogger(bool log_to_system, bool log_to_file) {
197 if (log_to_system) {
Yifan Honge7574202020-04-06 15:08:24 -0700198 if (kSideload) {
199 // No logd in sideload. Use stdout.
200 // recovery has already redirected stdio properly.
201 loggers_.push_back(__android_log_stderr_logger);
202 } else {
203 loggers_.push_back(__android_log_logd_logger);
204 }
Yifan Hongf1fba652020-03-02 13:03:25 -0800205 }
206 if (log_to_file) {
207 loggers_.push_back(std::move(FileLogger(SetupLogFile(kSystemLogsRoot))));
208 }
209 }
Tom Cherry35c71f72020-03-06 16:37:45 -0800210 void operator()(const struct __android_log_message* log_message) {
Kelvin Zhang7f0429f2023-04-04 16:39:35 -0700211 if (log_message->file != nullptr && log_message->line != 0) {
212 __android_log_message formatted = *log_message;
213 std::stringstream ss;
214 ss << "[" << LogPriorityToCString(formatted.priority) << ":"
215 << formatted.file << "(" << formatted.line << ")] "
216 << formatted.message;
217 formatted.file = nullptr;
218 formatted.line = 0;
219 const auto str = ss.str();
220 formatted.message = str.c_str();
221 for (auto&& logger : loggers_) {
222 logger(&formatted);
223 }
224 } else {
225 for (auto&& logger : loggers_) {
226 logger(log_message);
227 }
Yifan Hongf1fba652020-03-02 13:03:25 -0800228 }
229 }
230
231 private:
232 std::vector<LoggerFunction> loggers_;
233};
234
Yifan Honge7574202020-04-06 15:08:24 -0700235// Redirect all libchrome logs to liblog using our custom handler that does
236// not call __android_log_write and explicitly write to stderr at the same
237// time. The preset CombinedLogger already writes to stderr properly.
238bool RedirectToLiblog(int severity,
239 const char* file,
240 int line,
241 size_t message_start,
242 const std::string& str_newline) {
243 android_LogPriority priority =
244 (severity < 0) ? ANDROID_LOG_VERBOSE : ANDROID_LOG_UNKNOWN;
245 switch (severity) {
246 case logging::LOG_INFO:
247 priority = ANDROID_LOG_INFO;
248 break;
249 case logging::LOG_WARNING:
250 priority = ANDROID_LOG_WARN;
251 break;
252 case logging::LOG_ERROR:
253 priority = ANDROID_LOG_ERROR;
254 break;
255 case logging::LOG_FATAL:
256 priority = ANDROID_LOG_FATAL;
257 break;
258 }
259 std::string_view sv = str_newline;
260 ignore_result(android::base::ConsumeSuffix(&sv, "\n"));
261 std::string str(sv.data(), sv.size());
Tianjie48b788f2021-03-09 21:07:02 -0800262
263 if (priority == ANDROID_LOG_FATAL) {
264 // Abort the program for priority FATAL. __android_log_assert will log the
265 // message to stderr and CombinedLogger.
266 __android_log_assert(nullptr, nullptr, "%s", str.c_str());
267 } else {
268 // This will eventually be redirected to CombinedLogger.
269 // Use nullptr as tag so that liblog infers log tag from getprogname().
Kelvin Zhangb86f89c2023-04-05 15:51:17 -0700270 if (file == nullptr || file[0] == 0 || line == 0 || message_start != 0) {
Kelvin Zhang7f0429f2023-04-04 16:39:35 -0700271 __android_log_write(priority, nullptr /* tag */, str.c_str());
272 } else {
273 __android_log_print(priority,
274 nullptr,
275 "[%s:%s(%d)] %s",
276 LogPriorityToCString(priority),
277 file,
278 line,
279 str.c_str());
280 }
Tianjie48b788f2021-03-09 21:07:02 -0800281 }
Yifan Honge7574202020-04-06 15:08:24 -0700282 return true;
283}
284
Yifan Hongc80de2d2020-02-25 17:13:53 -0800285} // namespace
286
287void SetupLogging(bool log_to_system, bool log_to_file) {
Yifan Hongf1fba652020-03-02 13:03:25 -0800288 // Note that libchrome logging uses liblog.
289 // By calling liblog's __android_log_set_logger function, all of libchrome
290 // (used by update_engine) / libbase / liblog (used by depended modules)
291 // logging eventually redirects to CombinedLogger.
292 static auto g_logger =
293 std::make_unique<CombinedLogger>(log_to_system, log_to_file);
Tom Cherry35c71f72020-03-06 16:37:45 -0800294 __android_log_set_logger([](const struct __android_log_message* log_message) {
295 (*g_logger)(log_message);
296 });
Yifan Hongf1fba652020-03-02 13:03:25 -0800297
298 // libchrome logging should not log to file.
Yifan Hongc80de2d2020-02-25 17:13:53 -0800299 logging::LoggingSettings log_settings;
300 log_settings.lock_log = logging::DONT_LOCK_LOG_FILE;
Yifan Honge7574202020-04-06 15:08:24 -0700301 log_settings.logging_dest =
302 static_cast<logging::LoggingDestination>(logging::LOG_NONE);
Yifan Hongc80de2d2020-02-25 17:13:53 -0800303 log_settings.log_file = nullptr;
Yifan Hongc80de2d2020-02-25 17:13:53 -0800304 logging::InitLogging(log_settings);
Yifan Hongf1fba652020-03-02 13:03:25 -0800305 logging::SetLogItems(false /* enable_process_id */,
306 false /* enable_thread_id */,
307 false /* enable_timestamp */,
308 false /* enable_tickcount */);
Yifan Honge7574202020-04-06 15:08:24 -0700309 logging::SetLogMessageHandler(&RedirectToLiblog);
Yifan Hongc80de2d2020-02-25 17:13:53 -0800310}
311
312} // namespace chromeos_update_engine