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 
UdevCollector()46 UdevCollector::UdevCollector()
47     : dev_coredump_directory_(kDefaultDevCoredumpDirectory) {}
48 
~UdevCollector()49 UdevCollector::~UdevCollector() {}
50 
HandleCrash(const std::string & udev_event)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 
ProcessUdevCrashLogs(const FilePath & crash_directory,const std::string & action,const std::string & kernel,const std::string & subsystem)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 
ProcessDevCoredump(const FilePath & crash_directory,int instance_number)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 
AppendDevCoredump(const FilePath & crash_directory,const FilePath & coredump_path,int instance_number)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 
ClearDevCoredump(const FilePath & coredump_path)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 
GetFailingDeviceDriverName(int instance_number)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