| // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "crash-reporter/udev_collector.h" |
| |
| #include <map> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/files/file_enumerator.h> |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/strings/string_split.h> |
| #include <base/strings/string_util.h> |
| #include <base/strings/stringprintf.h> |
| #include <brillo/process/process.h> |
| |
| #include "crash-reporter/util.h" |
| |
| using base::FilePath; |
| |
| namespace { |
| |
| const char kCollectUdevSignature[] = "crash_reporter-udev-collection"; |
| const char kUdevExecName[] = "udev"; |
| const char kUdevSignatureKey[] = "sig"; |
| const char kUdevSubsystemDevCoredump[] = "devcoredump"; |
| const char kDefaultDevCoredumpDirectory[] = "/sys/class/devcoredump"; |
| const char kDevCoredumpFilePrefixFormat[] = "devcoredump_%s"; |
| |
| } // namespace |
| |
| UdevCollector::UdevCollector() |
| : CrashCollector("udev"), |
| dev_coredump_directory_(kDefaultDevCoredumpDirectory) {} |
| |
| UdevCollector::~UdevCollector() {} |
| |
| bool UdevCollector::IsSafeDevCoredump( |
| std::map<std::string, std::string> udev_event_map) { |
| // Is it a device coredump? |
| if (udev_event_map["SUBSYSTEM"] != kUdevSubsystemDevCoredump) |
| return false; |
| |
| int instance_number; |
| if (!base::StringToInt(udev_event_map["KERNEL_NUMBER"], &instance_number)) { |
| LOG(ERROR) << "Invalid kernel number: " << udev_event_map["KERNEL_NUMBER"]; |
| return false; |
| } |
| |
| // Retrieve the driver name of the failing device. |
| std::string driver_name = GetFailingDeviceDriverName(instance_number); |
| if (driver_name.empty()) { |
| LOG(ERROR) << "Failed to obtain driver name for instance: " |
| << instance_number; |
| return false; |
| } |
| |
| // Check for safe drivers: |
| return driver_name == "msm"; |
| } |
| |
| bool UdevCollector::HandleCrash(const std::string& udev_event) { |
| // Process the udev event string. |
| // First get all the key-value pairs. |
| std::vector<std::pair<std::string, std::string>> udev_event_keyval; |
| base::SplitStringIntoKeyValuePairs(udev_event, '=', ':', &udev_event_keyval); |
| std::map<std::string, std::string> udev_event_map; |
| for (const auto& key_value : udev_event_keyval) { |
| udev_event_map[key_value.first] = key_value.second; |
| } |
| |
| if (UdevCollector::IsSafeDevCoredump(udev_event_map)) { |
| LOG(INFO) << "Safe device coredumps are always processed"; |
| } else if (util::IsDeveloperImage()) { |
| LOG(INFO) << "developer image - collect udev crash info."; |
| } else if (udev_event_map["SUBSYSTEM"] == kUdevSubsystemDevCoredump) { |
| LOG(INFO) << "Device coredumps are not processed on non-developer images."; |
| FilePath coredump_path = FilePath( |
| base::StringPrintf("%s/devcd%s/data", dev_coredump_directory_.c_str(), |
| udev_event_map["KERNEL_NUMBER"].c_str())); |
| // Clear devcoredump memory before returning. |
| ClearDevCoredump(coredump_path); |
| return false; |
| } else { |
| LOG(INFO) << "Consent given - collect udev crash info."; |
| } |
| |
| // Make sure the crash directory exists, or create it if it doesn't. |
| FilePath crash_directory; |
| if (!GetCreatedCrashDirectoryByEuid(0, &crash_directory, nullptr)) { |
| LOG(ERROR) << "Could not get crash directory."; |
| return false; |
| } |
| |
| if (udev_event_map["SUBSYSTEM"] == kUdevSubsystemDevCoredump) { |
| int instance_number; |
| if (!base::StringToInt(udev_event_map["KERNEL_NUMBER"], &instance_number)) { |
| LOG(ERROR) << "Invalid kernel number: " |
| << udev_event_map["KERNEL_NUMBER"]; |
| return false; |
| } |
| return ProcessDevCoredump(crash_directory, instance_number); |
| } |
| |
| return ProcessUdevCrashLogs(crash_directory, udev_event_map["ACTION"], |
| udev_event_map["KERNEL"], |
| udev_event_map["SUBSYSTEM"]); |
| } |
| |
| bool UdevCollector::ProcessUdevCrashLogs(const FilePath& crash_directory, |
| const std::string& action, |
| const std::string& kernel, |
| const std::string& subsystem) { |
| // Construct the basename string for crash_reporter_logs.conf: |
| // "crash_reporter-udev-collection-[action]-[name]-[subsystem]" |
| // If a udev field is not provided, "" is used in its place, e.g.: |
| // "crash_reporter-udev-collection-[action]--[subsystem]" |
| // Hence, "" is used as a wildcard name string. |
| // TODO(sque, crosbug.com/32238): Implement wildcard checking. |
| std::string basename = action + "-" + kernel + "-" + subsystem; |
| std::string udev_log_name = |
| std::string(kCollectUdevSignature) + '-' + basename; |
| |
| // Create the destination path. |
| std::string log_file_name = FormatDumpBasename(basename, time(nullptr), 0); |
| FilePath crash_path = GetCrashPath(crash_directory, log_file_name, "log.gz"); |
| |
| // Handle the crash. |
| bool result = GetLogContents(log_config_path_, udev_log_name, crash_path); |
| if (!result) { |
| LOG(ERROR) << "Error reading udev log info " << udev_log_name; |
| return false; |
| } |
| |
| std::string exec_name = std::string(kUdevExecName) + "-" + subsystem; |
| AddCrashMetaData(kUdevSignatureKey, udev_log_name); |
| FinishCrash(GetCrashPath(crash_directory, log_file_name, "meta"), exec_name, |
| crash_path.BaseName().value()); |
| return true; |
| } |
| |
| bool UdevCollector::ProcessDevCoredump(const FilePath& crash_directory, |
| int instance_number) { |
| FilePath coredump_path = FilePath(base::StringPrintf( |
| "%s/devcd%d/data", dev_coredump_directory_.c_str(), instance_number)); |
| if (!base::PathExists(coredump_path)) { |
| LOG(ERROR) << "Device coredump file " << coredump_path.value() |
| << " does not exist"; |
| return false; |
| } |
| |
| // Add coredump file to the crash directory. |
| if (!AppendDevCoredump(crash_directory, coredump_path, instance_number)) { |
| ClearDevCoredump(coredump_path); |
| return false; |
| } |
| |
| // Clear the coredump data to allow generation of future device coredumps |
| // without having to wait for the 5-minutes timeout. |
| return ClearDevCoredump(coredump_path); |
| } |
| |
| bool UdevCollector::AppendDevCoredump(const FilePath& crash_directory, |
| const FilePath& coredump_path, |
| int instance_number) { |
| // Retrieve the driver name of the failing device. |
| std::string driver_name = GetFailingDeviceDriverName(instance_number); |
| if (driver_name.empty()) { |
| LOG(ERROR) << "Failed to obtain driver name for instance: " |
| << instance_number; |
| return false; |
| } |
| |
| std::string coredump_prefix = |
| base::StringPrintf(kDevCoredumpFilePrefixFormat, driver_name.c_str()); |
| |
| std::string dump_basename = |
| FormatDumpBasename(coredump_prefix, time(nullptr), instance_number); |
| FilePath core_path = GetCrashPath(crash_directory, dump_basename, "devcore"); |
| FilePath log_path = GetCrashPath(crash_directory, dump_basename, "log"); |
| FilePath meta_path = GetCrashPath(crash_directory, dump_basename, "meta"); |
| |
| // Collect coredump data. |
| if (!base::CopyFile(coredump_path, core_path)) { |
| PLOG(ERROR) << "Failed to copy device coredump file from " |
| << coredump_path.value() << " to " << core_path.value(); |
| return false; |
| } |
| |
| // Collect additional logs if one is specified in the config file. |
| std::string udev_log_name = std::string(kCollectUdevSignature) + '-' + |
| kUdevSubsystemDevCoredump + '-' + driver_name; |
| bool result = GetLogContents(log_config_path_, udev_log_name, log_path); |
| if (result) { |
| AddCrashMetaUploadFile("logs", log_path.BaseName().value()); |
| } |
| |
| AddCrashMetaData(kUdevSignatureKey, udev_log_name); |
| |
| FinishCrash(meta_path, coredump_prefix, core_path.BaseName().value()); |
| |
| return true; |
| } |
| |
| bool UdevCollector::ClearDevCoredump(const FilePath& coredump_path) { |
| if (!base::WriteFile(coredump_path, "0", 1)) { |
| PLOG(ERROR) << "Failed to delete the coredump data file " |
| << coredump_path.value(); |
| return false; |
| } |
| return true; |
| } |
| |
| FilePath UdevCollector::GetFailingDeviceDriverPath( |
| int instance_number, const std::string& sub_path) { |
| const FilePath dev_coredump_path(dev_coredump_directory_); |
| FilePath failing_uevent_path = dev_coredump_path.Append( |
| base::StringPrintf("devcd%d/%s", instance_number, sub_path.c_str())); |
| return failing_uevent_path; |
| } |
| |
| std::string UdevCollector::ExtractFailingDeviceDriverName( |
| const FilePath& failing_uevent_path) { |
| if (!base::PathExists(failing_uevent_path)) { |
| LOG(ERROR) << "Failing uevent path " << failing_uevent_path.value() |
| << " does not exist"; |
| return ""; |
| } |
| |
| std::string uevent_content; |
| if (!base::ReadFileToString(failing_uevent_path, &uevent_content)) { |
| PLOG(ERROR) << "Failed to read uevent file " << failing_uevent_path.value(); |
| return ""; |
| } |
| |
| // Parse uevent file contents as key-value pairs. |
| std::vector<std::pair<std::string, std::string>> uevent_keyval; |
| base::SplitStringIntoKeyValuePairs(uevent_content, '=', '\n', &uevent_keyval); |
| for (const auto& key_value : uevent_keyval) { |
| if (key_value.first == "DRIVER") { |
| return key_value.second; |
| } |
| } |
| |
| return ""; |
| } |
| |
| std::string UdevCollector::GetFailingDeviceDriverName(int instance_number) { |
| FilePath failing_uevent_path = |
| GetFailingDeviceDriverPath(instance_number, "failing_device/uevent"); |
| std::string name = ExtractFailingDeviceDriverName(failing_uevent_path); |
| if (name.empty()) { |
| LOG(WARNING) |
| << "Failed to obtain driver name; trying alternate uevent paths."; |
| failing_uevent_path = GetFailingDeviceDriverPath( |
| instance_number, "failing_device/device/uevent"); |
| name = ExtractFailingDeviceDriverName(failing_uevent_path); |
| } |
| return name; |
| } |