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 <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