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 17 // 18 // Strictly to deal with reboot into system after OTA after /data 19 // mounts to pull the last pmsg file data and place it 20 // into /data/misc/recovery/ directory, rotating it in. 21 // 22 // Usage: recovery-persist [--force-persist] 23 // 24 // On systems without /cache mount, all file content representing in the 25 // recovery/ directory stored in /sys/fs/pstore/pmsg-ramoops-0 in logger 26 // format that reside in the LOG_ID_SYSTEM buffer at ANDROID_LOG_INFO 27 // priority or higher is transfered to the /data/misc/recovery/ directory. 28 // The content is matched and rotated in as need be. 29 // 30 // --force-persist ignore /cache mount, always rotate in the contents. 31 // 32 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <unistd.h> 37 38 #include <limits> 39 #include <string> 40 41 #include <android-base/file.h> 42 #include <android-base/logging.h> 43 #include <metricslogger/metrics_logger.h> 44 #include <private/android_logger.h> /* private pmsg functions */ 45 46 #include "otautil/logging.h" 47 #include "otautil/parse_install_logs.h" 48 49 constexpr const char* LAST_LOG_FILE = "/data/misc/recovery/last_log"; 50 constexpr const char* LAST_PMSG_FILE = "/sys/fs/pstore/pmsg-ramoops-0"; 51 constexpr const char* LAST_KMSG_FILE = "/data/misc/recovery/last_kmsg"; 52 constexpr const char* LAST_CONSOLE_FILE = "/sys/fs/pstore/console-ramoops-0"; 53 constexpr const char* ALT_LAST_CONSOLE_FILE = "/sys/fs/pstore/console-ramoops"; 54 55 // close a file, log an error if the error indicator is set 56 static void check_and_fclose(FILE *fp, const char *name) { 57 fflush(fp); 58 if (ferror(fp)) { 59 PLOG(ERROR) << "Error in " << name; 60 } 61 fclose(fp); 62 } 63 64 static void copy_file(const char* source, const char* destination) { 65 FILE* dest_fp = fopen(destination, "we"); 66 if (dest_fp == nullptr) { 67 PLOG(ERROR) << "Can't open " << destination; 68 } else { 69 FILE* source_fp = fopen(source, "re"); 70 if (source_fp != nullptr) { 71 char buf[4096]; 72 size_t bytes; 73 while ((bytes = fread(buf, 1, sizeof(buf), source_fp)) != 0) { 74 fwrite(buf, 1, bytes, dest_fp); 75 } 76 check_and_fclose(source_fp, source); 77 } 78 check_and_fclose(dest_fp, destination); 79 } 80 } 81 82 static bool rotated = false; 83 84 ssize_t logsave( 85 log_id_t /* logId */, 86 char /* prio */, 87 const char *filename, 88 const char *buf, size_t len, 89 void * /* arg */) { 90 91 std::string destination("/data/misc/"); 92 destination += filename; 93 94 std::string buffer(buf, len); 95 96 { 97 std::string content; 98 android::base::ReadFileToString(destination, &content); 99 100 if (buffer.compare(content) == 0) { 101 return len; 102 } 103 } 104 105 // ToDo: Any others that match? Are we pulling in multiple 106 // already-rotated files? Algorithm thus far is KISS: one file, 107 // one rotation allowed. 108 109 rotate_logs(LAST_LOG_FILE, LAST_KMSG_FILE); 110 rotated = true; 111 112 return android::base::WriteStringToFile(buffer, destination.c_str()); 113 } 114 115 // Parses the LAST_INSTALL file and reports the update metrics saved under recovery mode. 116 static void report_metrics_from_last_install(const std::string& file_name) { 117 auto metrics = ParseLastInstall(file_name); 118 // TODO(xunchang) report the installation result. 119 for (const auto& [event, value] : metrics) { 120 if (value > std::numeric_limits<int>::max()) { 121 LOG(WARNING) << event << " (" << value << ") exceeds integer max."; 122 } else { 123 LOG(INFO) << "Uploading " << value << " to " << event; 124 android::metricslogger::LogHistogram(event, value); 125 } 126 } 127 } 128 129 int main(int argc, char **argv) { 130 131 /* Is /cache a mount?, we have been delivered where we are not wanted */ 132 /* 133 * Following code halves the size of the executable as compared to: 134 * 135 * load_volume_table(); 136 * has_cache = volume_for_path(CACHE_ROOT) != nullptr; 137 */ 138 bool has_cache = false; 139 static const char mounts_file[] = "/proc/mounts"; 140 FILE* fp = fopen(mounts_file, "re"); 141 if (!fp) { 142 PLOG(ERROR) << "failed to open " << mounts_file; 143 } else { 144 char *line = NULL; 145 size_t len = 0; 146 ssize_t read; 147 while ((read = getline(&line, &len, fp)) != -1) { 148 if (strstr(line, " /cache ")) { 149 has_cache = true; 150 break; 151 } 152 } 153 free(line); 154 fclose(fp); 155 } 156 157 if (has_cache) { 158 // Collects and reports the non-a/b update metrics from last_install; and removes the file 159 // to avoid duplicate report. 160 report_metrics_from_last_install(LAST_INSTALL_FILE_IN_CACHE); 161 if (access(LAST_INSTALL_FILE_IN_CACHE, F_OK) && unlink(LAST_INSTALL_FILE_IN_CACHE) == -1) { 162 PLOG(ERROR) << "Failed to unlink " << LAST_INSTALL_FILE_IN_CACHE; 163 } 164 165 // TBD: Future location to move content from /cache/recovery to /data/misc/recovery/ 166 // if --force-persist flag, then transfer pmsg data anyways 167 if ((argc <= 1) || !argv[1] || strcmp(argv[1], "--force-persist")) { 168 return 0; 169 } 170 } 171 172 /* Is there something in pmsg? */ 173 if (access(LAST_PMSG_FILE, R_OK)) { 174 return 0; 175 } 176 177 // Take last pmsg file contents and send it off to the logsave 178 __android_log_pmsg_file_read( 179 LOG_ID_SYSTEM, ANDROID_LOG_INFO, "recovery/", logsave, NULL); 180 181 // For those device without /cache, the last_install file has been copied to 182 // /data/misc/recovery from pmsg. Looks for the sideload history only. 183 if (!has_cache) { 184 report_metrics_from_last_install(LAST_INSTALL_FILE); 185 if (access(LAST_INSTALL_FILE, F_OK) && unlink(LAST_INSTALL_FILE) == -1) { 186 PLOG(ERROR) << "Failed to unlink " << LAST_INSTALL_FILE; 187 } 188 } 189 190 /* Is there a last console log too? */ 191 if (rotated) { 192 if (!access(LAST_CONSOLE_FILE, R_OK)) { 193 copy_file(LAST_CONSOLE_FILE, LAST_KMSG_FILE); 194 } else if (!access(ALT_LAST_CONSOLE_FILE, R_OK)) { 195 copy_file(ALT_LAST_CONSOLE_FILE, LAST_KMSG_FILE); 196 } 197 } 198 199 return 0; 200 } 201