1 //
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>
23 #include <functional>
24 #include <iomanip>
25 #include <sstream>
26 #include <string>
27 #include <string_view>
28 #include <vector>
29
30 #include <android-base/file.h>
31 #include <android-base/strings.h>
32 #include <android-base/unique_fd.h>
33 #include <base/files/dir_reader_posix.h>
34 #include <base/logging.h>
35 #include <android-base/stringprintf.h>
36 #include <log/log.h>
37
38 #include "android/log.h"
39 #include "update_engine/common/utils.h"
40
41 using std::string;
42
43 #ifdef _UE_SIDELOAD
44 constexpr bool kSideload = true;
45 #else
46 constexpr bool kSideload = false;
47 #endif
48
49 namespace chromeos_update_engine {
50 namespace {
51
52 constexpr char kSystemLogsRoot[] = "/data/misc/update_engine_log";
53 constexpr size_t kLogCount = 5;
54
55 // Keep the most recent |kLogCount| logs but remove the old ones in
56 // "/data/misc/update_engine_log/".
DeleteOldLogs(const string & kLogsRoot)57 void DeleteOldLogs(const string& kLogsRoot) {
58 base::DirReaderPosix reader(kLogsRoot.c_str());
59 if (!reader.IsValid()) {
60 LOG(ERROR) << "Failed to read " << kLogsRoot;
61 return;
62 }
63
64 std::vector<string> old_logs;
65 while (reader.Next()) {
66 if (reader.name()[0] == '.')
67 continue;
68
69 // Log files are in format "update_engine.%Y%m%d-%H%M%S",
70 // e.g. update_engine.20090103-231425
71 uint64_t date;
72 uint64_t local_time;
73 if (sscanf(reader.name(),
74 "update_engine.%" PRIu64 "-%" PRIu64 "",
75 &date,
76 &local_time) == 2) {
77 old_logs.push_back(reader.name());
78 } else {
79 LOG(WARNING) << "Unrecognized log file " << reader.name();
80 }
81 }
82
83 std::sort(old_logs.begin(), old_logs.end(), std::greater<string>());
84 for (size_t i = kLogCount; i < old_logs.size(); i++) {
85 string log_path = kLogsRoot + "/" + old_logs[i];
86 if (unlink(log_path.c_str()) == -1) {
87 PLOG(WARNING) << "Failed to unlink " << log_path;
88 }
89 }
90 }
91
SetupLogFile(const string & kLogsRoot)92 string SetupLogFile(const string& kLogsRoot) {
93 DeleteOldLogs(kLogsRoot);
94
95 return android::base::StringPrintf(
96 "%s/update_engine.%s",
97 kLogsRoot.c_str(),
98 utils::GetTimeAsString(::time(nullptr)).c_str());
99 }
100
LogPriorityToCString(int priority)101 const 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
120 using LoggerFunction = std::function<void(const struct __android_log_message*)>;
121
122 class FileLogger {
123 public:
FileLogger(const string & path)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,
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.
FileLogger(const FileLogger & other)146 FileLogger(const FileLogger& other) { fd_.reset(dup(other.fd_)); }
operator ()(const struct __android_log_message * log_message)147 void operator()(const struct __android_log_message* log_message) {
148 if (fd_ == -1) {
149 return;
150 }
151
152 std::string_view message_str =
153 log_message->message != nullptr ? log_message->message : "";
154
155 WriteToFd(GetPrefix(log_message));
156 WriteToFd(message_str);
157 WriteToFd("\n");
158 fsync(fd_);
159 }
160
161 private:
162 android::base::unique_fd fd_;
WriteToFd(std::string_view message)163 void WriteToFd(std::string_view message) {
164 ignore_result(
165 android::base::WriteFully(fd_, message.data(), message.size()));
166 }
167
GetPrefix(const struct __android_log_message * log_message)168 string GetPrefix(const struct __android_log_message* log_message) {
169 std::stringstream ss;
170 timeval tv;
171 gettimeofday(&tv, nullptr);
172 time_t t = tv.tv_sec;
173 struct tm local_time;
174 localtime_r(&t, &local_time);
175 struct tm* tm_time = &local_time;
176 ss << "[" << std::setfill('0') << std::setw(2) << 1 + tm_time->tm_mon
177 << std::setw(2) << tm_time->tm_mday << '/' << std::setw(2)
178 << tm_time->tm_hour << std::setw(2) << tm_time->tm_min << std::setw(2)
179 << tm_time->tm_sec << '.' << std::setw(6) << tv.tv_usec << "] ";
180 // libchrome logs prepends |message| with severity, file and line, but
181 // leave logger_data->file as nullptr.
182 // libbase / liblog logs doesn't. Hence, add them to match the style.
183 // For liblog logs that doesn't set logger_data->file, not printing the
184 // priority is acceptable.
185 if (log_message->file) {
186 ss << "[" << LogPriorityToCString(log_message->priority) << ':'
187 << log_message->file << '(' << log_message->line << ")] ";
188 }
189 return ss.str();
190 }
191 };
192
193 class CombinedLogger {
194 public:
CombinedLogger(bool log_to_system,bool log_to_file)195 CombinedLogger(bool log_to_system, bool log_to_file) {
196 if (log_to_system) {
197 if (kSideload) {
198 // No logd in sideload. Use stdout.
199 // recovery has already redirected stdio properly.
200 loggers_.push_back(__android_log_stderr_logger);
201 } else {
202 loggers_.push_back(__android_log_logd_logger);
203 }
204 }
205 if (log_to_file) {
206 loggers_.push_back(std::move(FileLogger(SetupLogFile(kSystemLogsRoot))));
207 }
208 }
operator ()(const struct __android_log_message * log_message)209 void operator()(const struct __android_log_message* log_message) {
210 if (log_message->file != nullptr && log_message->line != 0) {
211 __android_log_message formatted = *log_message;
212 std::stringstream ss;
213 ss << "[" << LogPriorityToCString(formatted.priority) << ":"
214 << formatted.file << "(" << formatted.line << ")] "
215 << formatted.message;
216 formatted.file = nullptr;
217 formatted.line = 0;
218 const auto str = ss.str();
219 formatted.message = str.c_str();
220 for (auto&& logger : loggers_) {
221 logger(&formatted);
222 }
223 } else {
224 for (auto&& logger : loggers_) {
225 logger(log_message);
226 }
227 }
228 }
229
230 private:
231 std::vector<LoggerFunction> loggers_;
232 };
233
234 // Redirect all libchrome logs to liblog using our custom handler that does
235 // not call __android_log_write and explicitly write to stderr at the same
236 // time. The preset CombinedLogger already writes to stderr properly.
RedirectToLiblog(int severity,const char * file,int line,size_t message_start,const std::string & str_newline)237 bool RedirectToLiblog(int severity,
238 const char* file,
239 int line,
240 size_t message_start,
241 const std::string& str_newline) {
242 android_LogPriority priority =
243 (severity < 0) ? ANDROID_LOG_VERBOSE : ANDROID_LOG_UNKNOWN;
244 switch (severity) {
245 case logging::LOG_INFO:
246 priority = ANDROID_LOG_INFO;
247 break;
248 case logging::LOG_WARNING:
249 priority = ANDROID_LOG_WARN;
250 break;
251 case logging::LOG_ERROR:
252 priority = ANDROID_LOG_ERROR;
253 break;
254 case logging::LOG_FATAL:
255 priority = ANDROID_LOG_FATAL;
256 break;
257 }
258 std::string_view sv = str_newline;
259 ignore_result(android::base::ConsumeSuffix(&sv, "\n"));
260 std::string str(sv.data(), sv.size());
261
262 if (priority == ANDROID_LOG_FATAL) {
263 // Abort the program for priority FATAL. __android_log_assert will log the
264 // message to stderr and CombinedLogger.
265 __android_log_assert(nullptr, nullptr, "%s", str.c_str());
266 } else {
267 // This will eventually be redirected to CombinedLogger.
268 // Use nullptr as tag so that liblog infers log tag from getprogname().
269 if (file == nullptr || file[0] == 0 || line == 0 || message_start != 0) {
270 __android_log_write(priority, nullptr /* tag */, str.c_str());
271 } else {
272 __android_log_print(priority,
273 nullptr,
274 "[%s:%s(%d)] %s",
275 LogPriorityToCString(priority),
276 file,
277 line,
278 str.c_str());
279 }
280 }
281 return true;
282 }
283
284 } // namespace
285
SetupLogging(bool log_to_system,bool log_to_file)286 void SetupLogging(bool log_to_system, bool log_to_file) {
287 // Note that libchrome logging uses liblog.
288 // By calling liblog's __android_log_set_logger function, all of libchrome
289 // (used by update_engine) / libbase / liblog (used by depended modules)
290 // logging eventually redirects to CombinedLogger.
291 static auto g_logger =
292 std::make_unique<CombinedLogger>(log_to_system, log_to_file);
293 __android_log_set_logger([](const struct __android_log_message* log_message) {
294 (*g_logger)(log_message);
295 });
296
297 // libchrome logging should not log to file.
298 logging::LoggingSettings log_settings;
299 log_settings.lock_log = logging::DONT_LOCK_LOG_FILE;
300 log_settings.logging_dest =
301 static_cast<logging::LoggingDestination>(logging::LOG_NONE);
302 log_settings.log_file = nullptr;
303 logging::InitLogging(log_settings);
304 logging::SetLogItems(false /* enable_process_id */,
305 false /* enable_thread_id */,
306 false /* enable_timestamp */,
307 false /* enable_tickcount */);
308 logging::SetLogMessageHandler(&RedirectToLiblog);
309 }
310
311 } // namespace chromeos_update_engine
312