1 /* 2 * Copyright (C) 2015 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 #define LOG_TAG "crash_collector" 18 19 #include <dirent.h> 20 #include <errno.h> 21 #include <sys/capability.h> 22 #include <unistd.h> 23 24 #include <string> 25 #include <vector> 26 27 #include <android-base/file.h> 28 #include <cutils/properties.h> 29 #include <log/logger.h> 30 #include <private/android_filesystem_config.h> 31 #include <utils/String8.h> 32 33 #include "client/linux/minidump_writer/linux_core_dumper.h" 34 #include "client/linux/minidump_writer/minidump_writer.h" 35 36 #undef DISALLOW_COPY_AND_ASSIGN // Defined in breakpad's header. 37 #include "coredump_writer.h" 38 39 namespace { 40 41 using android::String8; 42 43 const char kOutputDirectory[] = "/data/system/crash_reports"; 44 const int kMaxNumReports = 16; 45 46 // Gets a list of entries under the specified directory. 47 bool ReadDirectory(const std::string& path, std::vector<dirent>* entries) { 48 std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(path.c_str()), closedir); 49 if (!dir) 50 return false; 51 while (struct dirent* entry = readdir(dir.get())) { 52 if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) 53 entries->push_back(*entry); 54 } 55 return true; 56 } 57 58 // Removes a file or a directory recursively. 59 bool RemoveRecursively(const std::string& path) { 60 if (unlink(path.c_str()) == 0) 61 return true; 62 if (errno != EISDIR) { 63 ALOGE("Failed to unlink: %s, errno = %d", path.c_str(), errno); 64 return false; 65 } 66 std::vector<dirent> entries; 67 if (!ReadDirectory(path, &entries)) 68 return false; 69 for (const auto& entry : entries) { 70 if (!RemoveRecursively(path + "/" + entry.d_name)) 71 return false; 72 } 73 return rmdir(path.c_str()) == 0; 74 } 75 76 // Makes room for the new crash report by deleting old files when necessary. 77 bool MakeRoomForNewReport() { 78 // Enumerate reports. 79 std::vector<dirent> entries; 80 if (!ReadDirectory(kOutputDirectory, &entries)) 81 return false; 82 83 std::vector<time_t> dump_mtimes; // Modification time of dump files. 84 std::vector<std::pair<time_t, String8>> all_files; 85 for (const auto& entry : entries) { 86 String8 filename = String8(kOutputDirectory).appendPath(entry.d_name); 87 struct stat attributes; 88 if (stat(filename.string(), &attributes)) 89 return false; 90 all_files.push_back(std::make_pair(attributes.st_mtime, filename)); 91 if (filename.getPathExtension() == ".dmp") 92 dump_mtimes.push_back(attributes.st_mtime); 93 } 94 95 // Remove old files. 96 if (dump_mtimes.size() >= kMaxNumReports) { 97 // Sort the vector (newer file comes first). 98 std::sort(dump_mtimes.rbegin(), dump_mtimes.rend()); 99 100 const time_t threshold = dump_mtimes[kMaxNumReports - 1]; 101 for (const auto& file : all_files) { 102 const time_t mtime = file.first; 103 const String8& filename = file.second; 104 if (mtime <= threshold) { 105 if (!RemoveRecursively(filename.string())) 106 return false; 107 } 108 } 109 } 110 return true; 111 } 112 113 // Returns the specified system property. 114 std::string GetSystemProperty(const std::string& key) { 115 char buf[PROPERTY_VALUE_MAX]; 116 property_get(key.c_str(), buf, ""); 117 return std::string(buf); 118 } 119 120 // Writes metadata as JSON file. 121 bool WriteMetadata(ssize_t result_coredump_size, 122 size_t coredump_size_limit, 123 size_t expected_coredump_size, 124 const std::string& pid, 125 const std::string& uid, 126 const std::string& gid, 127 const std::string& signal, 128 const std::string& username, 129 const std::string& exec_name, 130 const std::string& filename) { 131 std::string content = "{"; 132 content += "\"version\":\"" + GetSystemProperty("ro.build.id") + "\""; 133 content += ","; 134 content += "\"result_coredump_size\":" + std::to_string(result_coredump_size); 135 content += ","; 136 content += "\"coredump_size_limit\":" + std::to_string(coredump_size_limit); 137 content += ","; 138 content += "\"expected_coredump_size\":" + 139 std::to_string(expected_coredump_size); 140 content += ","; 141 content += "\"pid\":" + pid; 142 content += ","; 143 content += "\"uid\":" + uid; 144 content += ","; 145 content += "\"gid\":" + gid; 146 content += ","; 147 content += "\"signal\":" + signal; 148 content += ","; 149 content += "\"username\":\"" + username + "\""; 150 content += ","; 151 content += "\"process\":\"" + exec_name + "\""; 152 content += "}"; 153 return android::base::WriteStringToFile( 154 content, filename, S_IRUSR | S_IWUSR, AID_SYSTEM, AID_SYSTEM); 155 } 156 157 // Converts the specified coredump file to a minidump. 158 bool ConvertCoredumpToMinidump(const std::string& coredump_filename, 159 const std::string& proc_files_dir, 160 const std::string& minidump_filename) { 161 google_breakpad::MappingList mappings; 162 google_breakpad::AppMemoryList memory_list; 163 google_breakpad::LinuxCoreDumper dumper( 164 0, coredump_filename.c_str(), proc_files_dir.c_str()); 165 bool success = google_breakpad::WriteMinidump( 166 minidump_filename.c_str(), mappings, memory_list, &dumper); 167 unlink(coredump_filename.c_str()); 168 RemoveRecursively(proc_files_dir); 169 return success; 170 } 171 172 } // namespace 173 174 int main(int argc, char** argv) { 175 if (argc < 7) { 176 ALOGE("Insufficient args."); 177 return 1; 178 } 179 const std::string pid_string = argv[1]; 180 const std::string uid_string = argv[2]; 181 const std::string gid_string = argv[3]; 182 const std::string signal_string = argv[4]; 183 const std::string crash_time = argv[5]; 184 const std::string exec_name = argv[6]; 185 186 const uid_t uid = std::stoi(uid_string); 187 const uid_t appid = uid % AID_USER; 188 if (appid >= AID_APP) { // Ignore non-system crashes. 189 return 0; 190 } 191 192 // Act as the system user and drop all capabilities. 193 struct __user_cap_header_struct capheader; 194 struct __user_cap_data_struct capdata[2]; 195 memset(&capheader, 0, sizeof(capheader)); 196 memset(&capdata, 0, sizeof(capdata)); 197 capheader.version = _LINUX_CAPABILITY_VERSION_3; 198 if (setegid(AID_SYSTEM) != 0 || seteuid(AID_SYSTEM) != 0 || 199 capset(&capheader, capdata) != 0) { 200 ALOGE("Failed to stop being root."); 201 return 1; 202 } 203 204 // Username lookup. 205 std::string username; 206 for (size_t i = 0; i < android_id_count; ++i) { 207 if (android_ids[i].aid == appid) { 208 username = android_ids[i].name; 209 break; 210 } 211 } 212 // Delete old crash reports. 213 if (!MakeRoomForNewReport()) { 214 ALOGE("Failed to delete old crash reports."); 215 return 1; 216 } 217 // Read coredump from stdin. 218 const std::string basename = 219 std::string(kOutputDirectory) + "/" + crash_time + "." + pid_string; 220 const std::string coredump = basename + ".core"; 221 const std::string proc_files_dir = basename + ".proc"; 222 if (mkdir(proc_files_dir.c_str(), S_IRUSR | S_IWUSR | S_IXUSR) == -1) { 223 ALOGE("Failed to create proc directory. errno = %d", errno); 224 return 1; 225 } 226 CoredumpWriter coredump_writer(STDIN_FILENO, coredump, proc_files_dir); 227 const ssize_t result_coredump_size = coredump_writer.WriteCoredump(); 228 if (result_coredump_size > 0) { 229 // Convert coredump to minidump. 230 const std::string minidump = basename + ".dmp"; 231 if (!ConvertCoredumpToMinidump(coredump, proc_files_dir, minidump)) { 232 ALOGE("Failed to convert coredump to minidump."); 233 } 234 } else { 235 ALOGE("Failed to copy coredump from stdin."); 236 } 237 // Write metadata. 238 const std::string metadata = basename + ".meta"; 239 if (!WriteMetadata(result_coredump_size, coredump_writer.coredump_size_limit(), 240 coredump_writer.expected_coredump_size(), pid_string, 241 uid_string, gid_string, signal_string, username, exec_name, 242 metadata)) { 243 ALOGE("Failed to write metadata."); 244 return 1; 245 } 246 return 0; 247 } 248