Home | History | Annotate | Download | only in crash_reporter
      1 /*
      2  * Copyright (C) 2012 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 #include "udev_collector.h"
     18 
     19 #include <map>
     20 #include <utility>
     21 #include <vector>
     22 
     23 #include <base/files/file_enumerator.h>
     24 #include <base/files/file_util.h>
     25 #include <base/logging.h>
     26 #include <base/strings/string_number_conversions.h>
     27 #include <base/strings/string_split.h>
     28 #include <base/strings/string_util.h>
     29 #include <base/strings/stringprintf.h>
     30 #include <brillo/process.h>
     31 
     32 using base::FilePath;
     33 
     34 namespace {
     35 
     36 const char kCollectUdevSignature[] = "crash_reporter-udev-collection";
     37 const char kGzipPath[] = "/bin/gzip";
     38 const char kUdevExecName[] = "udev";
     39 const char kUdevSignatureKey[] = "sig";
     40 const char kUdevSubsystemDevCoredump[] = "devcoredump";
     41 const char kDefaultDevCoredumpDirectory[] = "/sys/class/devcoredump";
     42 const char kDevCoredumpFilePrefixFormat[] = "devcoredump_%s";
     43 
     44 }  // namespace
     45 
     46 UdevCollector::UdevCollector()
     47     : dev_coredump_directory_(kDefaultDevCoredumpDirectory) {}
     48 
     49 UdevCollector::~UdevCollector() {}
     50 
     51 bool UdevCollector::HandleCrash(const std::string &udev_event) {
     52   if (IsDeveloperImage()) {
     53     LOG(INFO) << "developer image - collect udev crash info.";
     54   } else if (is_feedback_allowed_function_()) {
     55     LOG(INFO) << "Consent given - collect udev crash info.";
     56   } else {
     57     LOG(INFO) << "Ignoring - Non-developer image and no consent given.";
     58     return false;
     59   }
     60 
     61   // Process the udev event string.
     62   // First get all the key-value pairs.
     63   std::vector<std::pair<std::string, std::string>> udev_event_keyval;
     64   base::SplitStringIntoKeyValuePairs(udev_event, '=', ':', &udev_event_keyval);
     65   std::vector<std::pair<std::string, std::string>>::const_iterator iter;
     66   std::map<std::string, std::string> udev_event_map;
     67   for (iter = udev_event_keyval.begin();
     68        iter != udev_event_keyval.end();
     69        ++iter) {
     70     udev_event_map[iter->first] = iter->second;
     71   }
     72 
     73   // Make sure the crash directory exists, or create it if it doesn't.
     74   FilePath crash_directory;
     75   if (!GetCreatedCrashDirectoryByEuid(0, &crash_directory, nullptr)) {
     76     LOG(ERROR) << "Could not get crash directory.";
     77     return false;
     78   }
     79 
     80   if (udev_event_map["SUBSYSTEM"] == kUdevSubsystemDevCoredump) {
     81     int instance_number;
     82     if (!base::StringToInt(udev_event_map["KERNEL_NUMBER"], &instance_number)) {
     83       LOG(ERROR) << "Invalid kernel number: "
     84                  << udev_event_map["KERNEL_NUMBER"];
     85       return false;
     86     }
     87     return ProcessDevCoredump(crash_directory, instance_number);
     88   }
     89 
     90   return ProcessUdevCrashLogs(crash_directory,
     91                               udev_event_map["ACTION"],
     92                               udev_event_map["KERNEL"],
     93                               udev_event_map["SUBSYSTEM"]);
     94 }
     95 
     96 bool UdevCollector::ProcessUdevCrashLogs(const FilePath& crash_directory,
     97                                          const std::string& action,
     98                                          const std::string& kernel,
     99                                          const std::string& subsystem) {
    100   // Construct the basename string for crash_reporter_logs.conf:
    101   //   "crash_reporter-udev-collection-[action]-[name]-[subsystem]"
    102   // If a udev field is not provided, "" is used in its place, e.g.:
    103   //   "crash_reporter-udev-collection-[action]--[subsystem]"
    104   // Hence, "" is used as a wildcard name string.
    105   // TODO(sque, crosbug.com/32238): Implement wildcard checking.
    106   std::string basename = action + "-" + kernel + "-" + subsystem;
    107   std::string udev_log_name = std::string(kCollectUdevSignature) + '-' +
    108                               basename;
    109 
    110   // Create the destination path.
    111   std::string log_file_name =
    112       FormatDumpBasename(basename, time(nullptr), 0);
    113   FilePath crash_path = GetCrashPath(crash_directory, log_file_name, "log");
    114 
    115   // Handle the crash.
    116   bool result = GetLogContents(log_config_path_, udev_log_name, crash_path);
    117   if (!result) {
    118     LOG(ERROR) << "Error reading udev log info " << udev_log_name;
    119     return false;
    120   }
    121 
    122   // Compress the output using gzip.
    123   brillo::ProcessImpl gzip_process;
    124   gzip_process.AddArg(kGzipPath);
    125   gzip_process.AddArg(crash_path.value());
    126   int process_result = gzip_process.Run();
    127   FilePath crash_path_zipped = FilePath(crash_path.value() + ".gz");
    128   // If the zip file was not created, use the uncompressed file.
    129   if (process_result != 0 || !base::PathExists(crash_path_zipped))
    130     LOG(ERROR) << "Could not create zip file " << crash_path_zipped.value();
    131   else
    132     crash_path = crash_path_zipped;
    133 
    134   std::string exec_name = std::string(kUdevExecName) + "-" + subsystem;
    135   AddCrashMetaData(kUdevSignatureKey, udev_log_name);
    136   WriteCrashMetaData(GetCrashPath(crash_directory, log_file_name, "meta"),
    137                      exec_name, crash_path.value());
    138   return true;
    139 }
    140 
    141 bool UdevCollector::ProcessDevCoredump(const FilePath& crash_directory,
    142                                        int instance_number) {
    143   FilePath coredump_path =
    144       FilePath(base::StringPrintf("%s/devcd%d/data",
    145                                   dev_coredump_directory_.c_str(),
    146                                   instance_number));
    147   if (!base::PathExists(coredump_path)) {
    148     LOG(ERROR) << "Device coredump file " << coredump_path.value()
    149                << " does not exist";
    150     return false;
    151   }
    152 
    153   // Add coredump file to the crash directory.
    154   if (!AppendDevCoredump(crash_directory, coredump_path, instance_number)) {
    155     ClearDevCoredump(coredump_path);
    156     return false;
    157   }
    158 
    159   // Clear the coredump data to allow generation of future device coredumps
    160   // without having to wait for the 5-minutes timeout.
    161   return ClearDevCoredump(coredump_path);
    162 }
    163 
    164 bool UdevCollector::AppendDevCoredump(const FilePath& crash_directory,
    165                                       const FilePath& coredump_path,
    166                                       int instance_number) {
    167   // Retrieve the driver name of the failing device.
    168   std::string driver_name = GetFailingDeviceDriverName(instance_number);
    169   if (driver_name.empty()) {
    170     LOG(ERROR) << "Failed to obtain driver name for instance: "
    171                << instance_number;
    172     return false;
    173   }
    174 
    175   std::string coredump_prefix =
    176       base::StringPrintf(kDevCoredumpFilePrefixFormat, driver_name.c_str());
    177 
    178   std::string dump_basename = FormatDumpBasename(coredump_prefix,
    179                                                  time(nullptr),
    180                                                  instance_number);
    181   FilePath core_path = GetCrashPath(crash_directory, dump_basename, "devcore");
    182   FilePath log_path = GetCrashPath(crash_directory, dump_basename, "log");
    183   FilePath meta_path = GetCrashPath(crash_directory, dump_basename, "meta");
    184 
    185   // Collect coredump data.
    186   if (!base::CopyFile(coredump_path, core_path)) {
    187     LOG(ERROR) << "Failed to copy device coredumpm file from "
    188                << coredump_path.value() << " to " << core_path.value();
    189     return false;
    190   }
    191 
    192   // Collect additional logs if one is specified in the config file.
    193   std::string udev_log_name = std::string(kCollectUdevSignature) + '-' +
    194       kUdevSubsystemDevCoredump + '-' + driver_name;
    195   bool result = GetLogContents(log_config_path_, udev_log_name, log_path);
    196   if (result) {
    197     AddCrashMetaUploadFile("logs", log_path.value());
    198   }
    199 
    200   WriteCrashMetaData(meta_path, coredump_prefix, core_path.value());
    201 
    202   return true;
    203 }
    204 
    205 bool UdevCollector::ClearDevCoredump(const FilePath& coredump_path) {
    206   if (!base::WriteFile(coredump_path, "0", 1)) {
    207     LOG(ERROR) << "Failed to delete the coredump data file "
    208                << coredump_path.value();
    209     return false;
    210   }
    211   return true;
    212 }
    213 
    214 std::string UdevCollector::GetFailingDeviceDriverName(int instance_number) {
    215   FilePath failing_uevent_path =
    216       FilePath(base::StringPrintf("%s/devcd%d/failing_device/uevent",
    217                                   dev_coredump_directory_.c_str(),
    218                                   instance_number));
    219   if (!base::PathExists(failing_uevent_path)) {
    220     LOG(ERROR) << "Failing uevent path " << failing_uevent_path.value()
    221                << " does not exist";
    222     return "";
    223   }
    224 
    225   std::string uevent_content;
    226   if (!base::ReadFileToString(failing_uevent_path, &uevent_content)) {
    227     LOG(ERROR) << "Failed to read uevent file " << failing_uevent_path.value();
    228     return "";
    229   }
    230 
    231   // Parse uevent file contents as key-value pairs.
    232   std::vector<std::pair<std::string, std::string>> uevent_keyval;
    233   base::SplitStringIntoKeyValuePairs(uevent_content, '=', '\n', &uevent_keyval);
    234   std::vector<std::pair<std::string, std::string>>::const_iterator iter;
    235   for (iter = uevent_keyval.begin();
    236        iter != uevent_keyval.end();
    237        ++iter) {
    238     if (iter->first == "DRIVER") {
    239       return iter->second;
    240     }
    241   }
    242 
    243   return "";
    244 }
    245