Steve Fung | 6c34c25 | 2015-08-20 00:27:30 -0700 | [diff] [blame] | 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 | */ |
Simon Que | acc7938 | 2012-05-04 18:10:09 -0700 | [diff] [blame] | 16 | |
Steve Fung | 129bea5 | 2015-07-23 13:11:15 -0700 | [diff] [blame] | 17 | #include "udev_collector.h" |
Simon Que | acc7938 | 2012-05-04 18:10:09 -0700 | [diff] [blame] | 18 | |
Ben Chan | 7e77690 | 2014-06-18 13:19:51 -0700 | [diff] [blame] | 19 | #include <map> |
| 20 | #include <utility> |
| 21 | #include <vector> |
| 22 | |
Peter Qiu | 6aa551e | 2015-03-06 11:42:04 -0800 | [diff] [blame] | 23 | #include <base/files/file_enumerator.h> |
Ben Chan | ab6cc90 | 2014-09-05 08:21:06 -0700 | [diff] [blame] | 24 | #include <base/files/file_util.h> |
Ben Chan | 7e77690 | 2014-06-18 13:19:51 -0700 | [diff] [blame] | 25 | #include <base/logging.h> |
Peter Qiu | 6aa551e | 2015-03-06 11:42:04 -0800 | [diff] [blame] | 26 | #include <base/strings/string_number_conversions.h> |
Ben Chan | 7e77690 | 2014-06-18 13:19:51 -0700 | [diff] [blame] | 27 | #include <base/strings/string_split.h> |
| 28 | #include <base/strings/string_util.h> |
Peter Qiu | 6aa551e | 2015-03-06 11:42:04 -0800 | [diff] [blame] | 29 | #include <base/strings/stringprintf.h> |
Alex Vakulenko | 74dc624 | 2015-10-13 09:23:34 -0700 | [diff] [blame] | 30 | #include <brillo/process.h> |
Simon Que | acc7938 | 2012-05-04 18:10:09 -0700 | [diff] [blame] | 31 | |
Simon Que | 9f90aca | 2013-02-19 17:19:52 -0800 | [diff] [blame] | 32 | using base::FilePath; |
| 33 | |
Peter Qiu | 6aa551e | 2015-03-06 11:42:04 -0800 | [diff] [blame] | 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) {} |
Simon Que | acc7938 | 2012-05-04 18:10:09 -0700 | [diff] [blame] | 48 | |
| 49 | UdevCollector::~UdevCollector() {} |
| 50 | |
| 51 | bool UdevCollector::HandleCrash(const std::string &udev_event) { |
Peter Qiu | 6aa551e | 2015-03-06 11:42:04 -0800 | [diff] [blame] | 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."; |
Simon Que | acc7938 | 2012-05-04 18:10:09 -0700 | [diff] [blame] | 58 | return false; |
| 59 | } |
| 60 | |
| 61 | // Process the udev event string. |
| 62 | // First get all the key-value pairs. |
Ben Chan | c764139 | 2014-09-21 18:53:40 -0700 | [diff] [blame] | 63 | std::vector<std::pair<std::string, std::string>> udev_event_keyval; |
Simon Que | acc7938 | 2012-05-04 18:10:09 -0700 | [diff] [blame] | 64 | base::SplitStringIntoKeyValuePairs(udev_event, '=', ':', &udev_event_keyval); |
Ben Chan | c764139 | 2014-09-21 18:53:40 -0700 | [diff] [blame] | 65 | std::vector<std::pair<std::string, std::string>>::const_iterator iter; |
Simon Que | acc7938 | 2012-05-04 18:10:09 -0700 | [diff] [blame] | 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 | |
Simon Que | acc7938 | 2012-05-04 18:10:09 -0700 | [diff] [blame] | 73 | // Make sure the crash directory exists, or create it if it doesn't. |
| 74 | FilePath crash_directory; |
Ben Chan | 262d798 | 2014-09-18 08:05:20 -0700 | [diff] [blame] | 75 | if (!GetCreatedCrashDirectoryByEuid(0, &crash_directory, nullptr)) { |
Simon Que | acc7938 | 2012-05-04 18:10:09 -0700 | [diff] [blame] | 76 | LOG(ERROR) << "Could not get crash directory."; |
| 77 | return false; |
| 78 | } |
Peter Qiu | 6aa551e | 2015-03-06 11:42:04 -0800 | [diff] [blame] | 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 | |
Simon Que | acc7938 | 2012-05-04 18:10:09 -0700 | [diff] [blame] | 110 | // Create the destination path. |
| 111 | std::string log_file_name = |
Ben Chan | 262d798 | 2014-09-18 08:05:20 -0700 | [diff] [blame] | 112 | FormatDumpBasename(basename, time(nullptr), 0); |
Simon Que | acc7938 | 2012-05-04 18:10:09 -0700 | [diff] [blame] | 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. |
Alex Vakulenko | 74dc624 | 2015-10-13 09:23:34 -0700 | [diff] [blame] | 123 | brillo::ProcessImpl gzip_process; |
Simon Que | acc7938 | 2012-05-04 18:10:09 -0700 | [diff] [blame] | 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. |
Mike Frysinger | a557c11 | 2014-02-05 22:55:39 -0500 | [diff] [blame] | 129 | if (process_result != 0 || !base::PathExists(crash_path_zipped)) |
Simon Que | acc7938 | 2012-05-04 18:10:09 -0700 | [diff] [blame] | 130 | LOG(ERROR) << "Could not create zip file " << crash_path_zipped.value(); |
| 131 | else |
| 132 | crash_path = crash_path_zipped; |
| 133 | |
Peter Qiu | 6aa551e | 2015-03-06 11:42:04 -0800 | [diff] [blame] | 134 | std::string exec_name = std::string(kUdevExecName) + "-" + subsystem; |
Yufeng Shen | 20cb7cc | 2012-11-02 14:21:21 -0400 | [diff] [blame] | 135 | AddCrashMetaData(kUdevSignatureKey, udev_log_name); |
Simon Que | acc7938 | 2012-05-04 18:10:09 -0700 | [diff] [blame] | 136 | WriteCrashMetaData(GetCrashPath(crash_directory, log_file_name, "meta"), |
Yufeng Shen | 20cb7cc | 2012-11-02 14:21:21 -0400 | [diff] [blame] | 137 | exec_name, crash_path.value()); |
Simon Que | acc7938 | 2012-05-04 18:10:09 -0700 | [diff] [blame] | 138 | return true; |
| 139 | } |
Peter Qiu | 6aa551e | 2015-03-06 11:42:04 -0800 | [diff] [blame] | 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 | } |