Add udev event-based crash reporting

This allows crash_reporter to handle nonfatal errors from the kernel
passed thru udev events.  There is a udev .rules file for invoking crash
reporter.  Currently it supports DRM error info logging.

BUG=chrome-os-partner:6492
TEST=Run:
"crash_reporter --udev=ACTION=change:KERNEL=card0:SUBSYSTEM=drm"
Check that a card0-drm log file has been created under /var/spool/crash

Change-Id: I05a1d508e5446988575b0c1924878b8e36ae46bb
Signed-off-by: Simon Que <sque@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/10618
diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc
index fc7a52c..6046dc5 100644
--- a/crash_reporter/crash_collector.cc
+++ b/crash_reporter/crash_collector.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,6 +14,7 @@
 #include <inttypes.h>
 
 #include <set>
+#include <vector>
 
 #include "base/eintr_wrapper.h"
 #include "base/file_util.h"
@@ -24,12 +25,16 @@
 
 static const char kCollectChromeFile[] =
     "/mnt/stateful_partition/etc/collect_chrome_crashes";
+static const char kCollectUdevSignature[] = "crash_reporter-udev-collection";
 static const char kCrashTestInProgressPath[] = "/tmp/crash-test-in-progress";
+static const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf";
 static const char kDefaultUserName[] = "chronos";
 static const char kLeaveCoreFile[] = "/root/.leave_core";
 static const char kLsbRelease[] = "/etc/lsb-release";
 static const char kShellPath[] = "/bin/sh";
 static const char kSystemCrashPath[] = "/var/spool/crash";
+static const char kUdevExecName[] = "udev";
+static const char kUdevSignatureKey[] = "sig";
 static const char kUserCrashPath[] = "/home/chronos/user/crash";
 
 // Directory mode of the user crash spool directory.
@@ -69,6 +74,59 @@
   is_feedback_allowed_function_ = is_feedback_allowed_function;
 }
 
+bool CrashCollector::HandleUdevCrash(const std::string &udev_event) {
+  // Process the udev event string.
+  // The udev string should be formatted as follows:
+  //   "ACTION=[action]:KERNEL=[name]:SUBSYSTEM=[subsystem]"
+  // The values don't have to be in any particular order.
+
+  // 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;
+  }
+
+  // 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.
+  std::string basename = udev_event_map["ACTION"] + "-" +
+                         udev_event_map["KERNEL"] + "-" +
+                         udev_event_map["SUBSYSTEM"];
+  std::string udev_log_name = std::string(kCollectUdevSignature) + '-' +
+                              basename;
+
+  // Make sure the crash directory exists, or create it if it doesn't.
+  FilePath crash_directory;
+  if (!GetCreatedCrashDirectoryByEuid(0, &crash_directory, NULL)) {
+    LOG(ERROR) << "Could not get crash directory.";
+    return false;
+  }
+  // Create the destination path.
+  std::string log_file_name =
+      FormatDumpBasename(basename, time(NULL), 0);
+  FilePath crash_path = GetCrashPath(crash_directory, log_file_name, "log");
+
+  // Handle the crash.
+  bool result = GetLogContents(FilePath(kDefaultLogConfig), udev_log_name,
+                               crash_path);
+  if (!result) {
+    LOG(ERROR) << "Error reading udev log info " << udev_log_name;
+    return false;
+  }
+
+  AddCrashMetaData(kUdevSignatureKey, kCollectUdevSignature);
+  WriteCrashMetaData(GetCrashPath(crash_directory, log_file_name, "meta"),
+                     kUdevExecName, crash_path.value());
+  return true;
+}
+
 int CrashCollector::WriteNewFile(const FilePath &filename,
                                  const char *data,
                                  int size) {