aboutsummaryrefslogtreecommitdiff
path: root/udev_collector.cc
blob: 1e018db4d0f86af661d0a9f26e1e5c4dac9e14b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
/*
 * 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 "";
}