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