blob: 1e018db4d0f86af661d0a9f26e1e5c4dac9e14b9 [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "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.h>
using base::FilePath;
namespace {
const char kCollectUdevSignature[] = "crash_reporter-udev-collection";
const char kGzipPath[] = "/bin/gzip";
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()
: dev_coredump_directory_(kDefaultDevCoredumpDirectory) {}
UdevCollector::~UdevCollector() {}
bool UdevCollector::HandleCrash(const std::string &udev_event) {
if (IsDeveloperImage()) {
LOG(INFO) << "developer image - collect udev crash info.";
} else if (is_feedback_allowed_function_()) {
LOG(INFO) << "Consent given - collect udev crash info.";
} else {
LOG(INFO) << "Ignoring - Non-developer image and no consent given.";
return false;
}
// 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::vector<std::pair<std::string, std::string>>::const_iterator iter;
std::map<std::string, std::string> udev_event_map;
for (iter = udev_event_keyval.begin();
iter != udev_event_keyval.end();
++iter) {
udev_event_map[iter->first] = iter->second;
}
// 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");
// 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;
}
// Compress the output using gzip.
brillo::ProcessImpl gzip_process;
gzip_process.AddArg(kGzipPath);
gzip_process.AddArg(crash_path.value());
int process_result = gzip_process.Run();
FilePath crash_path_zipped = FilePath(crash_path.value() + ".gz");
// If the zip file was not created, use the uncompressed file.
if (process_result != 0 || !base::PathExists(crash_path_zipped))
LOG(ERROR) << "Could not create zip file " << crash_path_zipped.value();
else
crash_path = crash_path_zipped;
std::string exec_name = std::string(kUdevExecName) + "-" + subsystem;
AddCrashMetaData(kUdevSignatureKey, udev_log_name);
WriteCrashMetaData(GetCrashPath(crash_directory, log_file_name, "meta"),
exec_name, crash_path.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)) {
LOG(ERROR) << "Failed to copy device coredumpm 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.value());
}
WriteCrashMetaData(meta_path, coredump_prefix, core_path.value());
return true;
}
bool UdevCollector::ClearDevCoredump(const FilePath& coredump_path) {
if (!base::WriteFile(coredump_path, "0", 1)) {
LOG(ERROR) << "Failed to delete the coredump data file "
<< coredump_path.value();
return false;
}
return true;
}
std::string UdevCollector::GetFailingDeviceDriverName(int instance_number) {
FilePath failing_uevent_path =
FilePath(base::StringPrintf("%s/devcd%d/failing_device/uevent",
dev_coredump_directory_.c_str(),
instance_number));
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)) {
LOG(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);
std::vector<std::pair<std::string, std::string>>::const_iterator iter;
for (iter = uevent_keyval.begin();
iter != uevent_keyval.end();
++iter) {
if (iter->first == "DRIVER") {
return iter->second;
}
}
return "";
}