blob: 5940f78534ef30ce1dba18df568420f375fdde84 [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>
36#include <base/strings/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
96 return base::StringPrintf("%s/update_engine.%s",
97 kLogsRoot.c_str(),
98 utils::GetTimeAsString(::time(nullptr)).c_str());
99}
100
Yifan Hongf1fba652020-03-02 13:03:25 -0800101const char* LogPriorityToCString(int priority) {
102 switch (priority) {
103 case ANDROID_LOG_VERBOSE:
104 return "VERBOSE";
105 case ANDROID_LOG_DEBUG:
106 return "DEBUG";
107 case ANDROID_LOG_INFO:
108 return "INFO";
109 case ANDROID_LOG_WARN:
110 return "WARN";
111 case ANDROID_LOG_ERROR:
112 return "ERROR";
113 case ANDROID_LOG_FATAL:
114 return "FATAL";
115 default:
116 return "UNKNOWN";
117 }
118}
119
Tom Cherry35c71f72020-03-06 16:37:45 -0800120using LoggerFunction = std::function<void(const struct __android_log_message*)>;
Yifan Hongf1fba652020-03-02 13:03:25 -0800121
122class FileLogger {
123 public:
124 explicit FileLogger(const string& path) {
125 fd_.reset(TEMP_FAILURE_RETRY(
126 open(path.c_str(),
127 O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_SYNC,
128 0644)));
129 if (fd_ == -1) {
130 // Use ALOGE that logs to logd before __android_log_set_logger.
131 ALOGE("Cannot open persistent log %s: %s", path.c_str(), strerror(errno));
132 return;
133 }
134 // The log file will have AID_LOG as group ID; this GID is inherited from
135 // the parent directory "/data/misc/update_engine_log" which sets the SGID
136 // bit.
137 if (fchmod(fd_.get(), 0640) == -1) {
138 // Use ALOGE that logs to logd before __android_log_set_logger.
139 ALOGE("Cannot chmod 0640 persistent log %s: %s",
140 path.c_str(),
141 strerror(errno));
142 return;
143 }
144 }
145 // Copy-constructor needed to be converted to std::function.
146 FileLogger(const FileLogger& other) { fd_.reset(dup(other.fd_)); }
Tom Cherry35c71f72020-03-06 16:37:45 -0800147 void operator()(const struct __android_log_message* log_message) {
Yifan Hongf1fba652020-03-02 13:03:25 -0800148 if (fd_ == -1) {
149 return;
150 }
151
Yifan Honge7574202020-04-06 15:08:24 -0700152 std::string_view message_str =
Tom Cherry35c71f72020-03-06 16:37:45 -0800153 log_message->message != nullptr ? log_message->message : "";
Yifan Hongf1fba652020-03-02 13:03:25 -0800154
Tom Cherry35c71f72020-03-06 16:37:45 -0800155 WriteToFd(GetPrefix(log_message));
Yifan Honge7574202020-04-06 15:08:24 -0700156 WriteToFd(message_str);
Yifan Hongf1fba652020-03-02 13:03:25 -0800157 WriteToFd("\n");
158 }
159
160 private:
161 android::base::unique_fd fd_;
162 void WriteToFd(std::string_view message) {
163 ignore_result(
164 android::base::WriteFully(fd_, message.data(), message.size()));
165 }
166
Tom Cherry35c71f72020-03-06 16:37:45 -0800167 string GetPrefix(const struct __android_log_message* log_message) {
Yifan Hongf1fba652020-03-02 13:03:25 -0800168 std::stringstream ss;
169 timeval tv;
170 gettimeofday(&tv, nullptr);
171 time_t t = tv.tv_sec;
172 struct tm local_time;
173 localtime_r(&t, &local_time);
174 struct tm* tm_time = &local_time;
175 ss << "[" << std::setfill('0') << std::setw(2) << 1 + tm_time->tm_mon
176 << std::setw(2) << tm_time->tm_mday << '/' << std::setw(2)
177 << tm_time->tm_hour << std::setw(2) << tm_time->tm_min << std::setw(2)
178 << tm_time->tm_sec << '.' << std::setw(6) << tv.tv_usec << "] ";
179 // libchrome logs prepends |message| with severity, file and line, but
180 // leave logger_data->file as nullptr.
181 // libbase / liblog logs doesn't. Hence, add them to match the style.
182 // For liblog logs that doesn't set logger_data->file, not printing the
183 // priority is acceptable.
Tom Cherry35c71f72020-03-06 16:37:45 -0800184 if (log_message->file) {
185 ss << "[" << LogPriorityToCString(log_message->priority) << ':'
186 << log_message->file << '(' << log_message->line << ")] ";
Yifan Hongf1fba652020-03-02 13:03:25 -0800187 }
188 return ss.str();
189 }
190};
191
192class CombinedLogger {
193 public:
194 CombinedLogger(bool log_to_system, bool log_to_file) {
195 if (log_to_system) {
Yifan Honge7574202020-04-06 15:08:24 -0700196 if (kSideload) {
197 // No logd in sideload. Use stdout.
198 // recovery has already redirected stdio properly.
199 loggers_.push_back(__android_log_stderr_logger);
200 } else {
201 loggers_.push_back(__android_log_logd_logger);
202 }
Yifan Hongf1fba652020-03-02 13:03:25 -0800203 }
204 if (log_to_file) {
205 loggers_.push_back(std::move(FileLogger(SetupLogFile(kSystemLogsRoot))));
206 }
207 }
Tom Cherry35c71f72020-03-06 16:37:45 -0800208 void operator()(const struct __android_log_message* log_message) {
Kelvin Zhang7f0429f2023-04-04 16:39:35 -0700209 if (log_message->file != nullptr && log_message->line != 0) {
210 __android_log_message formatted = *log_message;
211 std::stringstream ss;
212 ss << "[" << LogPriorityToCString(formatted.priority) << ":"
213 << formatted.file << "(" << formatted.line << ")] "
214 << formatted.message;
215 formatted.file = nullptr;
216 formatted.line = 0;
217 const auto str = ss.str();
218 formatted.message = str.c_str();
219 for (auto&& logger : loggers_) {
220 logger(&formatted);
221 }
222 } else {
223 for (auto&& logger : loggers_) {
224 logger(log_message);
225 }
Yifan Hongf1fba652020-03-02 13:03:25 -0800226 }
227 }
228
229 private:
230 std::vector<LoggerFunction> loggers_;
231};
232
Yifan Honge7574202020-04-06 15:08:24 -0700233// Redirect all libchrome logs to liblog using our custom handler that does
234// not call __android_log_write and explicitly write to stderr at the same
235// time. The preset CombinedLogger already writes to stderr properly.
236bool RedirectToLiblog(int severity,
237 const char* file,
238 int line,
239 size_t message_start,
240 const std::string& str_newline) {
241 android_LogPriority priority =
242 (severity < 0) ? ANDROID_LOG_VERBOSE : ANDROID_LOG_UNKNOWN;
243 switch (severity) {
244 case logging::LOG_INFO:
245 priority = ANDROID_LOG_INFO;
246 break;
247 case logging::LOG_WARNING:
248 priority = ANDROID_LOG_WARN;
249 break;
250 case logging::LOG_ERROR:
251 priority = ANDROID_LOG_ERROR;
252 break;
253 case logging::LOG_FATAL:
254 priority = ANDROID_LOG_FATAL;
255 break;
256 }
257 std::string_view sv = str_newline;
258 ignore_result(android::base::ConsumeSuffix(&sv, "\n"));
259 std::string str(sv.data(), sv.size());
Tianjie48b788f2021-03-09 21:07:02 -0800260
261 if (priority == ANDROID_LOG_FATAL) {
262 // Abort the program for priority FATAL. __android_log_assert will log the
263 // message to stderr and CombinedLogger.
264 __android_log_assert(nullptr, nullptr, "%s", str.c_str());
265 } else {
266 // This will eventually be redirected to CombinedLogger.
267 // Use nullptr as tag so that liblog infers log tag from getprogname().
Kelvin Zhangb86f89c2023-04-05 15:51:17 -0700268 if (file == nullptr || file[0] == 0 || line == 0 || message_start != 0) {
Kelvin Zhang7f0429f2023-04-04 16:39:35 -0700269 __android_log_write(priority, nullptr /* tag */, str.c_str());
270 } else {
271 __android_log_print(priority,
272 nullptr,
273 "[%s:%s(%d)] %s",
274 LogPriorityToCString(priority),
275 file,
276 line,
277 str.c_str());
278 }
Tianjie48b788f2021-03-09 21:07:02 -0800279 }
Yifan Honge7574202020-04-06 15:08:24 -0700280 return true;
281}
282
Yifan Hongc80de2d2020-02-25 17:13:53 -0800283} // namespace
284
285void SetupLogging(bool log_to_system, bool log_to_file) {
Yifan Hongf1fba652020-03-02 13:03:25 -0800286 // Note that libchrome logging uses liblog.
287 // By calling liblog's __android_log_set_logger function, all of libchrome
288 // (used by update_engine) / libbase / liblog (used by depended modules)
289 // logging eventually redirects to CombinedLogger.
290 static auto g_logger =
291 std::make_unique<CombinedLogger>(log_to_system, log_to_file);
Tom Cherry35c71f72020-03-06 16:37:45 -0800292 __android_log_set_logger([](const struct __android_log_message* log_message) {
293 (*g_logger)(log_message);
294 });
Yifan Hongf1fba652020-03-02 13:03:25 -0800295
296 // libchrome logging should not log to file.
Yifan Hongc80de2d2020-02-25 17:13:53 -0800297 logging::LoggingSettings log_settings;
298 log_settings.lock_log = logging::DONT_LOCK_LOG_FILE;
Yifan Honge7574202020-04-06 15:08:24 -0700299 log_settings.logging_dest =
300 static_cast<logging::LoggingDestination>(logging::LOG_NONE);
Yifan Hongc80de2d2020-02-25 17:13:53 -0800301 log_settings.log_file = nullptr;
Yifan Hongc80de2d2020-02-25 17:13:53 -0800302 logging::InitLogging(log_settings);
Yifan Hongf1fba652020-03-02 13:03:25 -0800303 logging::SetLogItems(false /* enable_process_id */,
304 false /* enable_thread_id */,
305 false /* enable_timestamp */,
306 false /* enable_tickcount */);
Yifan Honge7574202020-04-06 15:08:24 -0700307 logging::SetLogMessageHandler(&RedirectToLiblog);
Yifan Hongc80de2d2020-02-25 17:13:53 -0800308}
309
310} // namespace chromeos_update_engine