Home | History | Annotate | Download | only in crash_collector
      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