Jerry Zhang | 152933a | 2018-05-02 16:56:00 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 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 | |
xunchang | 316e971 | 2019-04-12 16:22:15 -0700 | [diff] [blame] | 17 | #include "otautil/logging.h" |
Jerry Zhang | 152933a | 2018-05-02 16:56:00 -0700 | [diff] [blame] | 18 | |
xunchang | 2239b9e | 2019-04-15 15:24:24 -0700 | [diff] [blame] | 19 | #include <dirent.h> |
| 20 | #include <errno.h> |
Jerry Zhang | 152933a | 2018-05-02 16:56:00 -0700 | [diff] [blame] | 21 | #include <stdio.h> |
| 22 | #include <string.h> |
| 23 | #include <sys/klog.h> |
| 24 | #include <sys/types.h> |
| 25 | |
xunchang | 2239b9e | 2019-04-15 15:24:24 -0700 | [diff] [blame] | 26 | #include <algorithm> |
| 27 | #include <memory> |
Jerry Zhang | 152933a | 2018-05-02 16:56:00 -0700 | [diff] [blame] | 28 | #include <string> |
| 29 | |
| 30 | #include <android-base/file.h> |
| 31 | #include <android-base/logging.h> |
| 32 | #include <android-base/parseint.h> |
| 33 | #include <android-base/stringprintf.h> |
xunchang | 2239b9e | 2019-04-15 15:24:24 -0700 | [diff] [blame] | 34 | #include <android-base/unique_fd.h> |
Jerry Zhang | 152933a | 2018-05-02 16:56:00 -0700 | [diff] [blame] | 35 | #include <private/android_filesystem_config.h> /* for AID_SYSTEM */ |
| 36 | #include <private/android_logger.h> /* private pmsg functions */ |
xunchang | 316e971 | 2019-04-12 16:22:15 -0700 | [diff] [blame] | 37 | #include <selinux/label.h> |
Jerry Zhang | 152933a | 2018-05-02 16:56:00 -0700 | [diff] [blame] | 38 | |
Jerry Zhang | 152933a | 2018-05-02 16:56:00 -0700 | [diff] [blame] | 39 | #include "otautil/dirutil.h" |
| 40 | #include "otautil/paths.h" |
xunchang | 2478885 | 2019-03-22 16:08:52 -0700 | [diff] [blame] | 41 | #include "otautil/roots.h" |
Jerry Zhang | 152933a | 2018-05-02 16:56:00 -0700 | [diff] [blame] | 42 | |
xunchang | 2239b9e | 2019-04-15 15:24:24 -0700 | [diff] [blame] | 43 | constexpr const char* LOG_FILE = "/cache/recovery/log"; |
| 44 | constexpr const char* LAST_INSTALL_FILE = "/cache/recovery/last_install"; |
| 45 | constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg"; |
| 46 | constexpr const char* LAST_LOG_FILE = "/cache/recovery/last_log"; |
Jerry Zhang | 152933a | 2018-05-02 16:56:00 -0700 | [diff] [blame] | 47 | |
xunchang | 2239b9e | 2019-04-15 15:24:24 -0700 | [diff] [blame] | 48 | constexpr const char* LAST_KMSG_FILTER = "recovery/last_kmsg"; |
| 49 | constexpr const char* LAST_LOG_FILTER = "recovery/last_log"; |
| 50 | |
| 51 | constexpr const char* CACHE_LOG_DIR = "/cache/recovery"; |
| 52 | |
| 53 | static struct selabel_handle* logging_sehandle; |
| 54 | |
| 55 | void SetLoggingSehandle(selabel_handle* handle) { |
| 56 | logging_sehandle = handle; |
| 57 | } |
Jerry Zhang | 152933a | 2018-05-02 16:56:00 -0700 | [diff] [blame] | 58 | |
| 59 | // fopen(3)'s the given file, by mounting volumes and making parent dirs as necessary. Returns the |
| 60 | // file pointer, or nullptr on error. |
xunchang | 316e971 | 2019-04-12 16:22:15 -0700 | [diff] [blame] | 61 | static FILE* fopen_path(const std::string& path, const char* mode, const selabel_handle* sehandle) { |
Yifan Hong | d81b8e3 | 2018-12-17 14:29:06 -0800 | [diff] [blame] | 62 | if (ensure_path_mounted(path) != 0) { |
Jerry Zhang | 152933a | 2018-05-02 16:56:00 -0700 | [diff] [blame] | 63 | LOG(ERROR) << "Can't mount " << path; |
| 64 | return nullptr; |
| 65 | } |
| 66 | |
| 67 | // When writing, try to create the containing directory, if necessary. Use generous permissions, |
| 68 | // the system (init.rc) will reset them. |
| 69 | if (strchr("wa", mode[0])) { |
| 70 | mkdir_recursively(path, 0777, true, sehandle); |
| 71 | } |
| 72 | return fopen(path.c_str(), mode); |
| 73 | } |
| 74 | |
| 75 | void check_and_fclose(FILE* fp, const std::string& name) { |
| 76 | fflush(fp); |
| 77 | if (fsync(fileno(fp)) == -1) { |
| 78 | PLOG(ERROR) << "Failed to fsync " << name; |
| 79 | } |
| 80 | if (ferror(fp)) { |
| 81 | PLOG(ERROR) << "Error in " << name; |
| 82 | } |
| 83 | fclose(fp); |
| 84 | } |
| 85 | |
| 86 | // close a file, log an error if the error indicator is set |
| 87 | ssize_t logbasename(log_id_t /* id */, char /* prio */, const char* filename, const char* /* buf */, |
| 88 | size_t len, void* arg) { |
| 89 | bool* do_rotate = static_cast<bool*>(arg); |
xunchang | 2239b9e | 2019-04-15 15:24:24 -0700 | [diff] [blame] | 90 | if (std::string(LAST_KMSG_FILTER).find(filename) != std::string::npos || |
| 91 | std::string(LAST_LOG_FILTER).find(filename) != std::string::npos) { |
Jerry Zhang | 152933a | 2018-05-02 16:56:00 -0700 | [diff] [blame] | 92 | *do_rotate = true; |
| 93 | } |
| 94 | return len; |
| 95 | } |
| 96 | |
| 97 | ssize_t logrotate(log_id_t id, char prio, const char* filename, const char* buf, size_t len, |
| 98 | void* arg) { |
| 99 | bool* do_rotate = static_cast<bool*>(arg); |
| 100 | if (!*do_rotate) { |
| 101 | return __android_log_pmsg_file_write(id, prio, filename, buf, len); |
| 102 | } |
| 103 | |
| 104 | std::string name(filename); |
| 105 | size_t dot = name.find_last_of('.'); |
| 106 | std::string sub = name.substr(0, dot); |
| 107 | |
xunchang | 2239b9e | 2019-04-15 15:24:24 -0700 | [diff] [blame] | 108 | if (std::string(LAST_KMSG_FILTER).find(sub) == std::string::npos && |
| 109 | std::string(LAST_LOG_FILTER).find(sub) == std::string::npos) { |
Jerry Zhang | 152933a | 2018-05-02 16:56:00 -0700 | [diff] [blame] | 110 | return __android_log_pmsg_file_write(id, prio, filename, buf, len); |
| 111 | } |
| 112 | |
| 113 | // filename rotation |
| 114 | if (dot == std::string::npos) { |
| 115 | name += ".1"; |
| 116 | } else { |
| 117 | std::string number = name.substr(dot + 1); |
| 118 | if (!isdigit(number[0])) { |
| 119 | name += ".1"; |
| 120 | } else { |
| 121 | size_t i; |
| 122 | if (!android::base::ParseUint(number, &i)) { |
| 123 | LOG(ERROR) << "failed to parse uint in " << number; |
| 124 | return -1; |
| 125 | } |
| 126 | name = sub + "." + std::to_string(i + 1); |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | return __android_log_pmsg_file_write(id, prio, name.c_str(), buf, len); |
| 131 | } |
| 132 | |
| 133 | // Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max. |
| 134 | // Similarly rename last_kmsg -> last_kmsg.1 -> ... -> last_kmsg.$max. |
| 135 | // Overwrite any existing last_log.$max and last_kmsg.$max. |
| 136 | void rotate_logs(const char* last_log_file, const char* last_kmsg_file) { |
| 137 | // Logs should only be rotated once. |
| 138 | static bool rotated = false; |
| 139 | if (rotated) { |
| 140 | return; |
| 141 | } |
| 142 | rotated = true; |
| 143 | |
| 144 | for (int i = KEEP_LOG_COUNT - 1; i >= 0; --i) { |
| 145 | std::string old_log = android::base::StringPrintf("%s", last_log_file); |
| 146 | if (i > 0) { |
| 147 | old_log += "." + std::to_string(i); |
| 148 | } |
| 149 | std::string new_log = android::base::StringPrintf("%s.%d", last_log_file, i + 1); |
| 150 | // Ignore errors if old_log doesn't exist. |
| 151 | rename(old_log.c_str(), new_log.c_str()); |
| 152 | |
| 153 | std::string old_kmsg = android::base::StringPrintf("%s", last_kmsg_file); |
| 154 | if (i > 0) { |
| 155 | old_kmsg += "." + std::to_string(i); |
| 156 | } |
| 157 | std::string new_kmsg = android::base::StringPrintf("%s.%d", last_kmsg_file, i + 1); |
| 158 | rename(old_kmsg.c_str(), new_kmsg.c_str()); |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | // Writes content to the current pmsg session. |
| 163 | static ssize_t __pmsg_write(const std::string& filename, const std::string& buf) { |
| 164 | return __android_log_pmsg_file_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, filename.c_str(), |
| 165 | buf.data(), buf.size()); |
| 166 | } |
| 167 | |
| 168 | void copy_log_file_to_pmsg(const std::string& source, const std::string& destination) { |
| 169 | std::string content; |
| 170 | android::base::ReadFileToString(source, &content); |
| 171 | __pmsg_write(destination, content); |
| 172 | } |
| 173 | |
| 174 | // How much of the temp log we have copied to the copy in cache. |
| 175 | static off_t tmplog_offset = 0; |
| 176 | |
| 177 | void reset_tmplog_offset() { |
| 178 | tmplog_offset = 0; |
| 179 | } |
| 180 | |
Tianjie Xu | 164c60a | 2019-05-15 13:59:39 -0700 | [diff] [blame] | 181 | static void copy_log_file(const std::string& source, const std::string& destination, bool append) { |
| 182 | FILE* dest_fp = fopen_path(destination, append ? "ae" : "we", logging_sehandle); |
Jerry Zhang | 152933a | 2018-05-02 16:56:00 -0700 | [diff] [blame] | 183 | if (dest_fp == nullptr) { |
| 184 | PLOG(ERROR) << "Can't open " << destination; |
| 185 | } else { |
| 186 | FILE* source_fp = fopen(source.c_str(), "re"); |
| 187 | if (source_fp != nullptr) { |
| 188 | if (append) { |
| 189 | fseeko(source_fp, tmplog_offset, SEEK_SET); // Since last write |
| 190 | } |
| 191 | char buf[4096]; |
| 192 | size_t bytes; |
| 193 | while ((bytes = fread(buf, 1, sizeof(buf), source_fp)) != 0) { |
| 194 | fwrite(buf, 1, bytes, dest_fp); |
| 195 | } |
| 196 | if (append) { |
| 197 | tmplog_offset = ftello(source_fp); |
| 198 | } |
| 199 | check_and_fclose(source_fp, source); |
| 200 | } |
| 201 | check_and_fclose(dest_fp, destination); |
| 202 | } |
| 203 | } |
| 204 | |
Tianjie Xu | 164c60a | 2019-05-15 13:59:39 -0700 | [diff] [blame] | 205 | void copy_logs(bool save_current_log) { |
xunchang | 316e971 | 2019-04-12 16:22:15 -0700 | [diff] [blame] | 206 | // We only rotate and record the log of the current session if explicitly requested. This usually |
| 207 | // happens after wipes, installation from BCB or menu selections. This is to avoid unnecessary |
Jerry Zhang | 152933a | 2018-05-02 16:56:00 -0700 | [diff] [blame] | 208 | // rotation (and possible deletion) of log files, if it does not do anything loggable. |
xunchang | 316e971 | 2019-04-12 16:22:15 -0700 | [diff] [blame] | 209 | if (!save_current_log) { |
Jerry Zhang | 152933a | 2018-05-02 16:56:00 -0700 | [diff] [blame] | 210 | return; |
| 211 | } |
| 212 | |
| 213 | // Always write to pmsg, this allows the OTA logs to be caught in `logcat -L`. |
| 214 | copy_log_file_to_pmsg(Paths::Get().temporary_log_file(), LAST_LOG_FILE); |
| 215 | copy_log_file_to_pmsg(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE); |
| 216 | |
| 217 | // We can do nothing for now if there's no /cache partition. |
Tianjie Xu | 164c60a | 2019-05-15 13:59:39 -0700 | [diff] [blame] | 218 | if (!HasCache()) { |
Jerry Zhang | 152933a | 2018-05-02 16:56:00 -0700 | [diff] [blame] | 219 | return; |
| 220 | } |
| 221 | |
| 222 | ensure_path_mounted(LAST_LOG_FILE); |
| 223 | ensure_path_mounted(LAST_KMSG_FILE); |
| 224 | rotate_logs(LAST_LOG_FILE, LAST_KMSG_FILE); |
| 225 | |
| 226 | // Copy logs to cache so the system can find out what happened. |
Tianjie Xu | 164c60a | 2019-05-15 13:59:39 -0700 | [diff] [blame] | 227 | copy_log_file(Paths::Get().temporary_log_file(), LOG_FILE, true); |
| 228 | copy_log_file(Paths::Get().temporary_log_file(), LAST_LOG_FILE, false); |
| 229 | copy_log_file(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE, false); |
Jerry Zhang | 152933a | 2018-05-02 16:56:00 -0700 | [diff] [blame] | 230 | save_kernel_log(LAST_KMSG_FILE); |
| 231 | chmod(LOG_FILE, 0600); |
| 232 | chown(LOG_FILE, AID_SYSTEM, AID_SYSTEM); |
| 233 | chmod(LAST_KMSG_FILE, 0600); |
| 234 | chown(LAST_KMSG_FILE, AID_SYSTEM, AID_SYSTEM); |
| 235 | chmod(LAST_LOG_FILE, 0640); |
| 236 | chmod(LAST_INSTALL_FILE, 0644); |
Tianjie Xu | 2b1a464 | 2018-09-06 11:58:55 -0700 | [diff] [blame] | 237 | chown(LAST_INSTALL_FILE, AID_SYSTEM, AID_SYSTEM); |
Jerry Zhang | 152933a | 2018-05-02 16:56:00 -0700 | [diff] [blame] | 238 | sync(); |
| 239 | } |
| 240 | |
| 241 | // Read from kernel log into buffer and write out to file. |
| 242 | void save_kernel_log(const char* destination) { |
| 243 | int klog_buf_len = klogctl(KLOG_SIZE_BUFFER, 0, 0); |
| 244 | if (klog_buf_len <= 0) { |
| 245 | PLOG(ERROR) << "Error getting klog size"; |
| 246 | return; |
| 247 | } |
| 248 | |
| 249 | std::string buffer(klog_buf_len, 0); |
| 250 | int n = klogctl(KLOG_READ_ALL, &buffer[0], klog_buf_len); |
| 251 | if (n == -1) { |
| 252 | PLOG(ERROR) << "Error in reading klog"; |
| 253 | return; |
| 254 | } |
| 255 | buffer.resize(n); |
| 256 | android::base::WriteStringToFile(buffer, destination); |
| 257 | } |
xunchang | 2239b9e | 2019-04-15 15:24:24 -0700 | [diff] [blame] | 258 | |
| 259 | std::vector<saved_log_file> ReadLogFilesToMemory() { |
| 260 | ensure_path_mounted("/cache"); |
| 261 | |
| 262 | struct dirent* de; |
| 263 | std::unique_ptr<DIR, decltype(&closedir)> d(opendir(CACHE_LOG_DIR), closedir); |
| 264 | if (!d) { |
| 265 | if (errno != ENOENT) { |
| 266 | PLOG(ERROR) << "Failed to opendir " << CACHE_LOG_DIR; |
| 267 | } |
| 268 | return {}; |
| 269 | } |
| 270 | |
| 271 | std::vector<saved_log_file> log_files; |
| 272 | while ((de = readdir(d.get())) != nullptr) { |
| 273 | if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) { |
| 274 | std::string path = android::base::StringPrintf("%s/%s", CACHE_LOG_DIR, de->d_name); |
| 275 | |
| 276 | struct stat sb; |
| 277 | if (stat(path.c_str(), &sb) != 0) { |
| 278 | PLOG(ERROR) << "Failed to stat " << path; |
| 279 | continue; |
| 280 | } |
| 281 | // Truncate files to 512kb |
| 282 | size_t read_size = std::min<size_t>(sb.st_size, 1 << 19); |
| 283 | std::string data(read_size, '\0'); |
| 284 | |
| 285 | android::base::unique_fd log_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY))); |
| 286 | if (log_fd == -1 || !android::base::ReadFully(log_fd, data.data(), read_size)) { |
| 287 | PLOG(ERROR) << "Failed to read log file " << path; |
| 288 | continue; |
| 289 | } |
| 290 | |
| 291 | log_files.emplace_back(saved_log_file{ path, sb, data }); |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | return log_files; |
| 296 | } |
| 297 | |
| 298 | bool RestoreLogFilesAfterFormat(const std::vector<saved_log_file>& log_files) { |
| 299 | // Re-create the log dir and write back the log entries. |
| 300 | if (ensure_path_mounted(CACHE_LOG_DIR) != 0) { |
| 301 | PLOG(ERROR) << "Failed to mount " << CACHE_LOG_DIR; |
| 302 | return false; |
| 303 | } |
| 304 | |
| 305 | if (mkdir_recursively(CACHE_LOG_DIR, 0777, false, logging_sehandle) != 0) { |
| 306 | PLOG(ERROR) << "Failed to create " << CACHE_LOG_DIR; |
| 307 | return false; |
| 308 | } |
| 309 | |
| 310 | for (const auto& log : log_files) { |
| 311 | if (!android::base::WriteStringToFile(log.data, log.name, log.sb.st_mode, log.sb.st_uid, |
| 312 | log.sb.st_gid)) { |
| 313 | PLOG(ERROR) << "Failed to write to " << log.name; |
| 314 | } |
| 315 | } |
| 316 | |
| 317 | // Any part of the log we'd copied to cache is now gone. |
| 318 | // Reset the pointer so we copy from the beginning of the temp |
| 319 | // log. |
| 320 | reset_tmplog_offset(); |
Tianjie Xu | 164c60a | 2019-05-15 13:59:39 -0700 | [diff] [blame] | 321 | copy_logs(true /* save_current_log */); |
xunchang | 2239b9e | 2019-04-15 15:24:24 -0700 | [diff] [blame] | 322 | |
| 323 | return true; |
| 324 | } |