blob: 1e018db4d0f86af661d0a9f26e1e5c4dac9e14b9 [file] [log] [blame]
Steve Fung6c34c252015-08-20 00:27:30 -07001/*
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 Queacc79382012-05-04 18:10:09 -070016
Steve Fung129bea52015-07-23 13:11:15 -070017#include "udev_collector.h"
Simon Queacc79382012-05-04 18:10:09 -070018
Ben Chan7e776902014-06-18 13:19:51 -070019#include <map>
20#include <utility>
21#include <vector>
22
Peter Qiu6aa551e2015-03-06 11:42:04 -080023#include <base/files/file_enumerator.h>
Ben Chanab6cc902014-09-05 08:21:06 -070024#include <base/files/file_util.h>
Ben Chan7e776902014-06-18 13:19:51 -070025#include <base/logging.h>
Peter Qiu6aa551e2015-03-06 11:42:04 -080026#include <base/strings/string_number_conversions.h>
Ben Chan7e776902014-06-18 13:19:51 -070027#include <base/strings/string_split.h>
28#include <base/strings/string_util.h>
Peter Qiu6aa551e2015-03-06 11:42:04 -080029#include <base/strings/stringprintf.h>
Alex Vakulenko74dc6242015-10-13 09:23:34 -070030#include <brillo/process.h>
Simon Queacc79382012-05-04 18:10:09 -070031
Simon Que9f90aca2013-02-19 17:19:52 -080032using base::FilePath;
33
Peter Qiu6aa551e2015-03-06 11:42:04 -080034namespace {
35
36const char kCollectUdevSignature[] = "crash_reporter-udev-collection";
37const char kGzipPath[] = "/bin/gzip";
38const char kUdevExecName[] = "udev";
39const char kUdevSignatureKey[] = "sig";
40const char kUdevSubsystemDevCoredump[] = "devcoredump";
41const char kDefaultDevCoredumpDirectory[] = "/sys/class/devcoredump";
42const char kDevCoredumpFilePrefixFormat[] = "devcoredump_%s";
43
44} // namespace
45
46UdevCollector::UdevCollector()
47 : dev_coredump_directory_(kDefaultDevCoredumpDirectory) {}
Simon Queacc79382012-05-04 18:10:09 -070048
49UdevCollector::~UdevCollector() {}
50
51bool UdevCollector::HandleCrash(const std::string &udev_event) {
Peter Qiu6aa551e2015-03-06 11:42:04 -080052 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 Queacc79382012-05-04 18:10:09 -070058 return false;
59 }
60
61 // Process the udev event string.
62 // First get all the key-value pairs.
Ben Chanc7641392014-09-21 18:53:40 -070063 std::vector<std::pair<std::string, std::string>> udev_event_keyval;
Simon Queacc79382012-05-04 18:10:09 -070064 base::SplitStringIntoKeyValuePairs(udev_event, '=', ':', &udev_event_keyval);
Ben Chanc7641392014-09-21 18:53:40 -070065 std::vector<std::pair<std::string, std::string>>::const_iterator iter;
Simon Queacc79382012-05-04 18:10:09 -070066 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 Queacc79382012-05-04 18:10:09 -070073 // Make sure the crash directory exists, or create it if it doesn't.
74 FilePath crash_directory;
Ben Chan262d7982014-09-18 08:05:20 -070075 if (!GetCreatedCrashDirectoryByEuid(0, &crash_directory, nullptr)) {
Simon Queacc79382012-05-04 18:10:09 -070076 LOG(ERROR) << "Could not get crash directory.";
77 return false;
78 }
Peter Qiu6aa551e2015-03-06 11:42:04 -080079
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
96bool 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 Queacc79382012-05-04 18:10:09 -0700110 // Create the destination path.
111 std::string log_file_name =
Ben Chan262d7982014-09-18 08:05:20 -0700112 FormatDumpBasename(basename, time(nullptr), 0);
Simon Queacc79382012-05-04 18:10:09 -0700113 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 Vakulenko74dc6242015-10-13 09:23:34 -0700123 brillo::ProcessImpl gzip_process;
Simon Queacc79382012-05-04 18:10:09 -0700124 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 Frysingera557c112014-02-05 22:55:39 -0500129 if (process_result != 0 || !base::PathExists(crash_path_zipped))
Simon Queacc79382012-05-04 18:10:09 -0700130 LOG(ERROR) << "Could not create zip file " << crash_path_zipped.value();
131 else
132 crash_path = crash_path_zipped;
133
Peter Qiu6aa551e2015-03-06 11:42:04 -0800134 std::string exec_name = std::string(kUdevExecName) + "-" + subsystem;
Yufeng Shen20cb7cc2012-11-02 14:21:21 -0400135 AddCrashMetaData(kUdevSignatureKey, udev_log_name);
Simon Queacc79382012-05-04 18:10:09 -0700136 WriteCrashMetaData(GetCrashPath(crash_directory, log_file_name, "meta"),
Yufeng Shen20cb7cc2012-11-02 14:21:21 -0400137 exec_name, crash_path.value());
Simon Queacc79382012-05-04 18:10:09 -0700138 return true;
139}
Peter Qiu6aa551e2015-03-06 11:42:04 -0800140
141bool 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
164bool 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
205bool 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
214std::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}