blob: dffca9d37af435e2837acf24f1aaff3a6c23c9bf [file] [log] [blame]
Chris Sosae4a86032010-06-16 17:08:34 -07001// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Ken Mixter03403162010-08-18 15:23:16 -07005#include "crash-reporter/user_collector.h"
6
Ken Mixter777484c2010-07-23 16:22:44 -07007#include <grp.h> // For struct group.
Ken Mixter1b8fe012011-01-25 13:33:05 -08008#include <pcrecpp.h>
Ken Mixter777484c2010-07-23 16:22:44 -07009#include <pwd.h> // For struct passwd.
Ken Mixter2953c3a2010-10-18 14:42:20 -070010#include <sys/types.h> // For getpwuid_r, getgrnam_r, WEXITSTATUS.
Ken Mixter777484c2010-07-23 16:22:44 -070011
Chris Sosae4a86032010-06-16 17:08:34 -070012#include <string>
Ken Mixter2953c3a2010-10-18 14:42:20 -070013#include <vector>
Chris Sosae4a86032010-06-16 17:08:34 -070014
15#include "base/file_util.h"
16#include "base/logging.h"
Chris Masone8a68c7c2011-05-14 11:44:04 -070017#include "base/string_split.h"
Chris Sosae4a86032010-06-16 17:08:34 -070018#include "base/string_util.h"
Ken Mixtera3249322011-03-03 08:47:38 -080019#include "chromeos/process.h"
20#include "chromeos/syslog_logging.h"
Ken Mixter207694d2010-10-28 15:42:37 -070021#include "gflags/gflags.h"
Chris Sosae4a86032010-06-16 17:08:34 -070022
Ken Mixterc6a58e02010-11-01 18:05:30 -070023#pragma GCC diagnostic ignored "-Wstrict-aliasing"
Ken Mixter1b8fe012011-01-25 13:33:05 -080024DEFINE_bool(core2md_failure, false, "Core2md failure test");
25DEFINE_bool(directory_failure, false, "Spool directory failure test");
Ken Mixterc6a58e02010-11-01 18:05:30 -070026DEFINE_string(filter_in, "",
27 "Ignore all crashes but this for testing");
28#pragma GCC diagnostic error "-Wstrict-aliasing"
Ken Mixter207694d2010-10-28 15:42:37 -070029
30static const char kCollectionErrorSignature[] =
31 "crash_reporter-user-collection";
Chris Sosae4a86032010-06-16 17:08:34 -070032// This procfs file is used to cause kernel core file writing to
33// instead pipe the core file into a user space process. See
34// core(5) man page.
35static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern";
Ken Mixterc49dbd42010-12-14 17:44:11 -080036static const char kCorePipeLimitFile[] = "/proc/sys/kernel/core_pipe_limit";
37// Set core_pipe_limit to 4 so that we can catch a few unrelated concurrent
38// crashes, but finite to avoid infinitely recursing on crash handling.
39static const char kCorePipeLimit[] = "4";
Ken Mixter777484c2010-07-23 16:22:44 -070040static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md";
Ken Mixter777484c2010-07-23 16:22:44 -070041
Ken Mixterc49dbd42010-12-14 17:44:11 -080042static const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf";
Ben Chanf13bb582012-01-06 08:22:07 -080043static const char kStatePrefix[] = "State:\t";
Ken Mixterc49dbd42010-12-14 17:44:11 -080044
Ken Mixter777484c2010-07-23 16:22:44 -070045const char *UserCollector::kUserId = "Uid:\t";
46const char *UserCollector::kGroupId = "Gid:\t";
Chris Sosae4a86032010-06-16 17:08:34 -070047
48UserCollector::UserCollector()
Ken Mixter777484c2010-07-23 16:22:44 -070049 : generate_diagnostics_(false),
50 core_pattern_file_(kCorePatternFile),
Ken Mixterc49dbd42010-12-14 17:44:11 -080051 core_pipe_limit_file_(kCorePipeLimitFile),
Ken Mixter03403162010-08-18 15:23:16 -070052 initialized_(false) {
Chris Sosae4a86032010-06-16 17:08:34 -070053}
54
55void UserCollector::Initialize(
56 UserCollector::CountCrashFunction count_crash_function,
57 const std::string &our_path,
58 UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function,
Ken Mixter777484c2010-07-23 16:22:44 -070059 bool generate_diagnostics) {
Ken Mixter03403162010-08-18 15:23:16 -070060 CrashCollector::Initialize(count_crash_function,
Ken Mixtera3249322011-03-03 08:47:38 -080061 is_feedback_allowed_function);
Chris Sosae4a86032010-06-16 17:08:34 -070062 our_path_ = our_path;
Chris Sosae4a86032010-06-16 17:08:34 -070063 initialized_ = true;
Ken Mixter777484c2010-07-23 16:22:44 -070064 generate_diagnostics_ = generate_diagnostics;
Chris Sosae4a86032010-06-16 17:08:34 -070065}
66
67UserCollector::~UserCollector() {
68}
69
70std::string UserCollector::GetPattern(bool enabled) const {
71 if (enabled) {
Ken Mixter1b8fe012011-01-25 13:33:05 -080072 // Combine the three crash attributes into one parameter to try to reduce
73 // the size of the invocation line for crash_reporter since the kernel
74 // has a fixed-sized (128B) buffer that it will truncate into. Note that
75 // the kernel does not support quoted arguments in core_pattern.
76 return StringPrintf("|%s --user=%%p:%%s:%%e", our_path_.c_str());
Chris Sosae4a86032010-06-16 17:08:34 -070077 } else {
78 return "core";
79 }
80}
81
82bool UserCollector::SetUpInternal(bool enabled) {
83 CHECK(initialized_);
Ken Mixtera3249322011-03-03 08:47:38 -080084 LOG(INFO) << (enabled ? "Enabling" : "Disabling") << " user crash handling";
85
Ken Mixterc49dbd42010-12-14 17:44:11 -080086 if (file_util::WriteFile(FilePath(core_pipe_limit_file_),
87 kCorePipeLimit,
88 strlen(kCorePipeLimit)) !=
89 static_cast<int>(strlen(kCorePipeLimit))) {
Ken Mixtera3249322011-03-03 08:47:38 -080090 LOG(ERROR) << "Unable to write " << core_pipe_limit_file_;
Ken Mixterc49dbd42010-12-14 17:44:11 -080091 return false;
92 }
Chris Sosae4a86032010-06-16 17:08:34 -070093 std::string pattern = GetPattern(enabled);
94 if (file_util::WriteFile(FilePath(core_pattern_file_),
95 pattern.c_str(),
96 pattern.length()) !=
97 static_cast<int>(pattern.length())) {
Ken Mixtera3249322011-03-03 08:47:38 -080098 LOG(ERROR) << "Unable to write " << core_pattern_file_;
Chris Sosae4a86032010-06-16 17:08:34 -070099 return false;
100 }
101 return true;
102}
103
Ken Mixter777484c2010-07-23 16:22:44 -0700104FilePath UserCollector::GetProcessPath(pid_t pid) {
105 return FilePath(StringPrintf("/proc/%d", pid));
106}
107
108bool UserCollector::GetSymlinkTarget(const FilePath &symlink,
109 FilePath *target) {
110 int max_size = 32;
111 scoped_array<char> buffer;
112 while (true) {
113 buffer.reset(new char[max_size + 1]);
114 ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size);
115 if (size < 0) {
Ken Mixterd49d3622011-02-09 18:23:00 -0800116 int saved_errno = errno;
Ken Mixtera3249322011-03-03 08:47:38 -0800117 LOG(ERROR) << "Readlink failed on " << symlink.value() << " with "
118 << saved_errno;
Ken Mixter777484c2010-07-23 16:22:44 -0700119 return false;
120 }
121 buffer[size] = 0;
122 if (size == max_size) {
123 // Avoid overflow when doubling.
124 if (max_size * 2 > max_size) {
125 max_size *= 2;
126 continue;
127 } else {
128 return false;
129 }
130 }
131 break;
132 }
133
134 *target = FilePath(buffer.get());
135 return true;
136}
137
138bool UserCollector::GetExecutableBaseNameFromPid(uid_t pid,
139 std::string *base_name) {
140 FilePath target;
Ken Mixterd49d3622011-02-09 18:23:00 -0800141 FilePath process_path = GetProcessPath(pid);
142 FilePath exe_path = process_path.Append("exe");
143 if (!GetSymlinkTarget(exe_path, &target)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800144 LOG(INFO) << "GetSymlinkTarget failed - Path " << process_path.value()
145 << " DirectoryExists: "
146 << file_util::DirectoryExists(process_path);
Ken Mixterd49d3622011-02-09 18:23:00 -0800147 // Try to further diagnose exe readlink failure cause.
148 struct stat buf;
149 int stat_result = stat(exe_path.value().c_str(), &buf);
150 int saved_errno = errno;
151 if (stat_result < 0) {
Ken Mixtera3249322011-03-03 08:47:38 -0800152 LOG(INFO) << "stat " << exe_path.value() << " failed: " << stat_result
153 << " " << saved_errno;
Ken Mixterd49d3622011-02-09 18:23:00 -0800154 } else {
Ken Mixtera3249322011-03-03 08:47:38 -0800155 LOG(INFO) << "stat " << exe_path.value() << " succeeded: st_mode="
156 << buf.st_mode;
Ken Mixterd49d3622011-02-09 18:23:00 -0800157 }
Ken Mixter777484c2010-07-23 16:22:44 -0700158 return false;
Ken Mixterd49d3622011-02-09 18:23:00 -0800159 }
Ken Mixter777484c2010-07-23 16:22:44 -0700160 *base_name = target.BaseName().value();
161 return true;
162}
163
Ben Chanf13bb582012-01-06 08:22:07 -0800164bool UserCollector::GetFirstLineWithPrefix(
165 const std::vector<std::string> &lines,
166 const char *prefix, std::string *line) {
167 std::vector<std::string>::const_iterator line_iterator;
168 for (line_iterator = lines.begin(); line_iterator != lines.end();
169 ++line_iterator) {
170 if (line_iterator->find(prefix) == 0) {
171 *line = *line_iterator;
172 return true;
173 }
174 }
175 return false;
176}
177
178bool UserCollector::GetIdFromStatus(
179 const char *prefix, IdKind kind,
180 const std::vector<std::string> &status_lines, int *id) {
Ken Mixter777484c2010-07-23 16:22:44 -0700181 // From fs/proc/array.c:task_state(), this file contains:
182 // \nUid:\t<uid>\t<euid>\t<suid>\t<fsuid>\n
Ben Chanf13bb582012-01-06 08:22:07 -0800183 std::string id_line;
184 if (!GetFirstLineWithPrefix(status_lines, prefix, &id_line)) {
Ken Mixter777484c2010-07-23 16:22:44 -0700185 return false;
186 }
Ben Chanf13bb582012-01-06 08:22:07 -0800187 std::string id_substring = id_line.substr(strlen(prefix), std::string::npos);
Ken Mixter777484c2010-07-23 16:22:44 -0700188 std::vector<std::string> ids;
Chris Masone3ba6c5b2011-05-13 16:57:09 -0700189 base::SplitString(id_substring, '\t', &ids);
Ken Mixter777484c2010-07-23 16:22:44 -0700190 if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) {
191 return false;
192 }
193 const char *number = ids[kind].c_str();
194 char *end_number = NULL;
195 *id = strtol(number, &end_number, 10);
Ben Chanf13bb582012-01-06 08:22:07 -0800196 if (*end_number != '\0') {
Ken Mixter777484c2010-07-23 16:22:44 -0700197 return false;
Ben Chanf13bb582012-01-06 08:22:07 -0800198 }
199 return true;
200}
201
202bool UserCollector::GetStateFromStatus(
203 const std::vector<std::string> &status_lines, std::string *state) {
204 std::string state_line;
205 if (!GetFirstLineWithPrefix(status_lines, kStatePrefix, &state_line)) {
206 return false;
207 }
208 *state = state_line.substr(strlen(kStatePrefix), std::string::npos);
Ken Mixter777484c2010-07-23 16:22:44 -0700209 return true;
210}
211
Ken Mixter207694d2010-10-28 15:42:37 -0700212void UserCollector::EnqueueCollectionErrorLog(pid_t pid,
213 const std::string &exec) {
214 FilePath crash_path;
Ken Mixtera3249322011-03-03 08:47:38 -0800215 LOG(INFO) << "Writing conversion problems as separate crash report.";
Ken Mixter207694d2010-10-28 15:42:37 -0700216 if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, NULL)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800217 LOG(ERROR) << "Could not even get log directory; out of space?";
Ken Mixter207694d2010-10-28 15:42:37 -0700218 return;
219 }
220 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
Ken Mixtera3249322011-03-03 08:47:38 -0800221 std::string error_log = chromeos::GetLog();
Ken Mixter1b8fe012011-01-25 13:33:05 -0800222 FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog");
223 if (GetLogContents(FilePath(kDefaultLogConfig), kCollectionErrorSignature,
224 diag_log_path)) {
225 // We load the contents of diag_log into memory and append it to
226 // the error log. We cannot just append to files because we need
227 // to always create new files to prevent attack.
228 std::string diag_log_contents;
229 file_util::ReadFileToString(diag_log_path, &diag_log_contents);
230 error_log.append(diag_log_contents);
231 file_util::Delete(diag_log_path, false);
232 }
Ken Mixter207694d2010-10-28 15:42:37 -0700233 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
234 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
Ken Mixter9b346472010-11-07 13:45:45 -0800235 // We must use WriteNewFile instead of file_util::WriteFile as we do
236 // not want to write with root access to a symlink that an attacker
237 // might have created.
Ken Mixter1b8fe012011-01-25 13:33:05 -0800238 WriteNewFile(log_path, error_log.data(), error_log.length());
Ken Mixter207694d2010-10-28 15:42:37 -0700239 AddCrashMetaData("sig", kCollectionErrorSignature);
240 WriteCrashMetaData(meta_path, exec, log_path.value());
241}
242
Ken Mixter777484c2010-07-23 16:22:44 -0700243bool UserCollector::CopyOffProcFiles(pid_t pid,
244 const FilePath &container_dir) {
245 if (!file_util::CreateDirectory(container_dir)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800246 LOG(ERROR) << "Could not create " << container_dir.value().c_str();
Ken Mixter777484c2010-07-23 16:22:44 -0700247 return false;
248 }
249 FilePath process_path = GetProcessPath(pid);
250 if (!file_util::PathExists(process_path)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800251 LOG(ERROR) << "Path " << process_path.value() << " does not exist";
Ken Mixter777484c2010-07-23 16:22:44 -0700252 return false;
253 }
254 static const char *proc_files[] = {
255 "auxv",
256 "cmdline",
257 "environ",
258 "maps",
259 "status"
260 };
261 for (unsigned i = 0; i < arraysize(proc_files); ++i) {
262 if (!file_util::CopyFile(process_path.Append(proc_files[i]),
263 container_dir.Append(proc_files[i]))) {
Ken Mixtera3249322011-03-03 08:47:38 -0800264 LOG(ERROR) << "Could not copy " << proc_files[i] << " file";
Ken Mixter777484c2010-07-23 16:22:44 -0700265 return false;
266 }
267 }
Ben Chanf13bb582012-01-06 08:22:07 -0800268 return ValidateProcFiles(container_dir);
269}
270
271bool UserCollector::ValidateProcFiles(const FilePath &container_dir) {
272 // Check if the maps file is empty, which could be due to the crashed
273 // process being reaped by the kernel before finishing a core dump.
274 int64 file_size = 0;
275 if (!file_util::GetFileSize(container_dir.Append("maps"), &file_size)) {
276 LOG(ERROR) << "Could not get the size of maps file";
277 return false;
278 }
279 if (file_size == 0) {
280 LOG(ERROR) << "maps file is empty";
281 return false;
282 }
Ken Mixter777484c2010-07-23 16:22:44 -0700283 return true;
284}
285
Ken Mixter777484c2010-07-23 16:22:44 -0700286bool UserCollector::GetCreatedCrashDirectory(pid_t pid,
Ken Mixter207694d2010-10-28 15:42:37 -0700287 FilePath *crash_file_path,
288 bool *out_of_capacity) {
Ken Mixter777484c2010-07-23 16:22:44 -0700289 FilePath process_path = GetProcessPath(pid);
290 std::string status;
Ken Mixter1b8fe012011-01-25 13:33:05 -0800291 if (FLAGS_directory_failure) {
Ken Mixtera3249322011-03-03 08:47:38 -0800292 LOG(ERROR) << "Purposefully failing to create spool directory";
Ken Mixter207694d2010-10-28 15:42:37 -0700293 return false;
294 }
Ken Mixter777484c2010-07-23 16:22:44 -0700295 if (!file_util::ReadFileToString(process_path.Append("status"),
296 &status)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800297 LOG(ERROR) << "Could not read status file";
298 LOG(INFO) << "Path " << process_path.value() << " DirectoryExists: "
299 << file_util::DirectoryExists(process_path);
Ken Mixter777484c2010-07-23 16:22:44 -0700300 return false;
301 }
Ben Chanf13bb582012-01-06 08:22:07 -0800302
303 std::vector<std::string> status_lines;
304 base::SplitString(status, '\n', &status_lines);
305
306 std::string process_state;
307 if (!GetStateFromStatus(status_lines, &process_state)) {
308 LOG(ERROR) << "Could not find process state in status file";
309 return false;
310 }
311 LOG(INFO) << "State of crashed process [" << pid << "]: " << process_state;
312
Ken Mixter777484c2010-07-23 16:22:44 -0700313 int process_euid;
Ben Chanf13bb582012-01-06 08:22:07 -0800314 if (!GetIdFromStatus(kUserId, kIdEffective, status_lines, &process_euid)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800315 LOG(ERROR) << "Could not find euid in status file";
Ken Mixter777484c2010-07-23 16:22:44 -0700316 return false;
317 }
Ken Mixter207694d2010-10-28 15:42:37 -0700318 if (!GetCreatedCrashDirectoryByEuid(process_euid,
319 crash_file_path,
320 out_of_capacity)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800321 LOG(ERROR) << "Could not create crash directory";
Ken Mixter207694d2010-10-28 15:42:37 -0700322 return false;
323 }
324 return true;
Ken Mixter777484c2010-07-23 16:22:44 -0700325}
326
327bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) {
328 // Copy off all stdin to a core file.
329 FilePath stdin_path("/dev/fd/0");
330 if (file_util::CopyFile(stdin_path, core_path)) {
331 return true;
332 }
333
Ken Mixtera3249322011-03-03 08:47:38 -0800334 LOG(ERROR) << "Could not write core file";
Ken Mixter777484c2010-07-23 16:22:44 -0700335 // If the file system was full, make sure we remove any remnants.
336 file_util::Delete(core_path, false);
337 return false;
338}
339
Ken Mixter207694d2010-10-28 15:42:37 -0700340bool UserCollector::RunCoreToMinidump(const FilePath &core_path,
341 const FilePath &procfs_directory,
342 const FilePath &minidump_path,
343 const FilePath &temp_directory) {
Ken Mixter777484c2010-07-23 16:22:44 -0700344 FilePath output_path = temp_directory.Append("output");
Ken Mixtera3249322011-03-03 08:47:38 -0800345 chromeos::ProcessImpl core2md;
346 core2md.RedirectOutput(output_path.value());
347 core2md.AddArg(kCoreToMinidumpConverterPath);
348 core2md.AddArg(core_path.value());
349 core2md.AddArg(procfs_directory.value());
Ken Mixter2953c3a2010-10-18 14:42:20 -0700350
Ken Mixtera3249322011-03-03 08:47:38 -0800351 if (!FLAGS_core2md_failure) {
352 core2md.AddArg(minidump_path.value());
353 } else {
Ken Mixter207694d2010-10-28 15:42:37 -0700354 // To test how core2md errors are propagaged, cause an error
355 // by forgetting a required argument.
Ken Mixter207694d2010-10-28 15:42:37 -0700356 }
357
Ken Mixtera3249322011-03-03 08:47:38 -0800358 int errorlevel = core2md.Run();
Ken Mixter777484c2010-07-23 16:22:44 -0700359
360 std::string output;
361 file_util::ReadFileToString(output_path, &output);
362 if (errorlevel != 0) {
Ken Mixtera3249322011-03-03 08:47:38 -0800363 LOG(ERROR) << "Problem during " << kCoreToMinidumpConverterPath
364 << " [result=" << errorlevel << "]: " << output;
Ken Mixter777484c2010-07-23 16:22:44 -0700365 return false;
366 }
367
368 if (!file_util::PathExists(minidump_path)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800369 LOG(ERROR) << "Minidump file " << minidump_path.value()
370 << " was not created";
Ken Mixter777484c2010-07-23 16:22:44 -0700371 return false;
372 }
373 return true;
374}
375
Ken Mixter207694d2010-10-28 15:42:37 -0700376bool UserCollector::ConvertCoreToMinidump(pid_t pid,
377 const FilePath &container_dir,
378 const FilePath &core_path,
379 const FilePath &minidump_path) {
Ken Mixter777484c2010-07-23 16:22:44 -0700380 if (!CopyOffProcFiles(pid, container_dir)) {
Ken Mixter777484c2010-07-23 16:22:44 -0700381 return false;
382 }
383
Ken Mixter777484c2010-07-23 16:22:44 -0700384 if (!CopyStdinToCoreFile(core_path)) {
Ken Mixter777484c2010-07-23 16:22:44 -0700385 return false;
386 }
387
Ken Mixter207694d2010-10-28 15:42:37 -0700388 bool conversion_result = RunCoreToMinidump(
389 core_path,
390 container_dir, // procfs directory
391 minidump_path,
392 container_dir); // temporary directory
Ken Mixteree849c52010-09-30 15:30:10 -0700393
Ken Mixter777484c2010-07-23 16:22:44 -0700394 if (conversion_result) {
Ken Mixtera3249322011-03-03 08:47:38 -0800395 LOG(INFO) << "Stored minidump to " << minidump_path.value();
Ken Mixter777484c2010-07-23 16:22:44 -0700396 }
397
Ken Mixter207694d2010-10-28 15:42:37 -0700398 return conversion_result;
399}
400
401bool UserCollector::ConvertAndEnqueueCrash(int pid,
402 const std::string &exec,
403 bool *out_of_capacity) {
404 FilePath crash_path;
405 if (!GetCreatedCrashDirectory(pid, &crash_path, out_of_capacity)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800406 LOG(ERROR) << "Unable to find/create process-specific crash path";
Ken Mixter207694d2010-10-28 15:42:37 -0700407 return false;
408 }
409
Ben Chan294d5d12012-01-04 20:40:15 -0800410 // Directory like /tmp/crash_reporter/1234 which contains the
Ken Mixter207694d2010-10-28 15:42:37 -0700411 // procfs entries and other temporary files used during conversion.
Ben Chan294d5d12012-01-04 20:40:15 -0800412 FilePath container_dir(StringPrintf("/tmp/crash_reporter/%d", pid));
Ken Mixter1b8fe012011-01-25 13:33:05 -0800413 // Delete a pre-existing directory from crash reporter that may have
414 // been left around for diagnostics from a failed conversion attempt.
415 // If we don't, existing files can cause forking to fail.
416 file_util::Delete(container_dir, true);
Ken Mixter207694d2010-10-28 15:42:37 -0700417 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
418 FilePath core_path = GetCrashPath(crash_path, dump_basename, "core");
419 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
420 FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp");
Ken Mixterc49dbd42010-12-14 17:44:11 -0800421 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
422
423 if (GetLogContents(FilePath(kDefaultLogConfig), exec, log_path))
424 AddCrashMetaData("log", log_path.value());
Ken Mixter207694d2010-10-28 15:42:37 -0700425
426 if (!ConvertCoreToMinidump(pid, container_dir, core_path,
427 minidump_path)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800428 LOG(INFO) << "Leaving core file at " << core_path.value()
429 << " due to conversion error";
Ken Mixter207694d2010-10-28 15:42:37 -0700430 return false;
431 }
432
433 // Here we commit to sending this file. We must not return false
434 // after this point or we will generate a log report as well as a
435 // crash report.
436 WriteCrashMetaData(meta_path,
437 exec,
438 minidump_path.value());
439
Michael Krebs538ecbf2011-07-27 14:13:22 -0700440 if (!IsDeveloperImage()) {
Ken Mixter777484c2010-07-23 16:22:44 -0700441 file_util::Delete(core_path, false);
442 } else {
Ken Mixtera3249322011-03-03 08:47:38 -0800443 LOG(INFO) << "Leaving core file at " << core_path.value()
444 << " due to developer image";
Ken Mixter777484c2010-07-23 16:22:44 -0700445 }
446
Ken Mixter207694d2010-10-28 15:42:37 -0700447 file_util::Delete(container_dir, true);
448 return true;
Ken Mixter777484c2010-07-23 16:22:44 -0700449}
450
Ken Mixter1b8fe012011-01-25 13:33:05 -0800451bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes,
452 pid_t *pid, int *signal,
453 std::string *kernel_supplied_name) {
454 pcrecpp::RE re("(\\d+):(\\d+):(.*)");
455 return re.FullMatch(crash_attributes, pid, signal, kernel_supplied_name);
456}
457
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700458bool UserCollector::ShouldDump(bool has_owner_consent,
459 bool is_developer,
Michael Krebs4fe30db2011-08-05 13:54:52 -0700460 bool handle_chrome_crashes,
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700461 const std::string &exec,
462 std::string *reason) {
463 reason->clear();
464
465 // Treat Chrome crashes as if the user opted-out. We stop counting Chrome
466 // crashes towards user crashes, so user crashes really mean non-Chrome
467 // user-space crashes.
Michael Krebs538ecbf2011-07-27 14:13:22 -0700468 if ((exec == "chrome" || exec == "supplied_chrome") &&
Michael Krebs4fe30db2011-08-05 13:54:52 -0700469 !handle_chrome_crashes) {
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700470 *reason = "ignoring - chrome crash";
471 return false;
472 }
473
474 // For developer builds, we always want to keep the crash reports unless
475 // we're testing the crash facilities themselves. This overrides
476 // feedback. Crash sending still obeys consent.
Michael Krebs538ecbf2011-07-27 14:13:22 -0700477 if (is_developer) {
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700478 *reason = "developer build - not testing - always dumping";
479 return true;
480 }
481
482 if (!has_owner_consent) {
483 *reason = "ignoring - no consent";
484 return false;
485 }
486
487 *reason = "handling";
488 return true;
489}
490
Ken Mixter1b8fe012011-01-25 13:33:05 -0800491bool UserCollector::HandleCrash(const std::string &crash_attributes,
492 const char *force_exec) {
Chris Sosae4a86032010-06-16 17:08:34 -0700493 CHECK(initialized_);
Ken Mixter1b8fe012011-01-25 13:33:05 -0800494 int pid = 0;
495 int signal = 0;
496 std::string kernel_supplied_name;
497
498 if (!ParseCrashAttributes(crash_attributes, &pid, &signal,
499 &kernel_supplied_name)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800500 LOG(ERROR) << "Invalid parameter: --user=" << crash_attributes;
Ken Mixter1b8fe012011-01-25 13:33:05 -0800501 return false;
502 }
503
Ken Mixter777484c2010-07-23 16:22:44 -0700504 std::string exec;
505 if (force_exec) {
506 exec.assign(force_exec);
507 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) {
Ken Mixter1b8fe012011-01-25 13:33:05 -0800508 // If we cannot find the exec name, use the kernel supplied name.
509 // We don't always use the kernel's since it truncates the name to
510 // 16 characters.
511 exec = StringPrintf("supplied_%s", kernel_supplied_name.c_str());
Ken Mixter777484c2010-07-23 16:22:44 -0700512 }
Ken Mixterc6a58e02010-11-01 18:05:30 -0700513
514 // Allow us to test the crash reporting mechanism successfully even if
515 // other parts of the system crash.
516 if (!FLAGS_filter_in.empty() &&
517 (FLAGS_filter_in == "none" ||
518 FLAGS_filter_in != exec)) {
519 // We use a different format message to make it more obvious in tests
520 // which crashes are test generated and which are real.
Ken Mixtera3249322011-03-03 08:47:38 -0800521 LOG(WARNING) << "Ignoring crash from " << exec << "[" << pid << "] while "
522 << "filter_in=" << FLAGS_filter_in << ".";
Ken Mixterc6a58e02010-11-01 18:05:30 -0700523 return true;
524 }
525
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700526 std::string reason;
527 bool dump = ShouldDump(is_feedback_allowed_function_(),
Michael Krebs538ecbf2011-07-27 14:13:22 -0700528 IsDeveloperImage(),
Michael Krebs4fe30db2011-08-05 13:54:52 -0700529 ShouldHandleChromeCrashes(),
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700530 exec,
531 &reason);
Ken Mixter2105b492010-11-09 16:14:38 -0800532
Ken Mixtera3249322011-03-03 08:47:38 -0800533 LOG(WARNING) << "Received crash notification for " << exec << "[" << pid
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700534 << "] sig " << signal << " (" << reason << ")";
Chris Sosae4a86032010-06-16 17:08:34 -0700535
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700536 if (dump) {
Chris Sosae4a86032010-06-16 17:08:34 -0700537 count_crash_function_();
Ken Mixter777484c2010-07-23 16:22:44 -0700538
Ken Mixter03403162010-08-18 15:23:16 -0700539 if (generate_diagnostics_) {
Ken Mixter207694d2010-10-28 15:42:37 -0700540 bool out_of_capacity = false;
Ken Mixter1b8fe012011-01-25 13:33:05 -0800541 bool convert_and_enqueue_result =
542 ConvertAndEnqueueCrash(pid, exec, &out_of_capacity);
Ken Mixter1b8fe012011-01-25 13:33:05 -0800543 if (!convert_and_enqueue_result) {
Ken Mixter207694d2010-10-28 15:42:37 -0700544 if (!out_of_capacity)
545 EnqueueCollectionErrorLog(pid, exec);
546 return false;
547 }
Ken Mixter03403162010-08-18 15:23:16 -0700548 }
Ken Mixter777484c2010-07-23 16:22:44 -0700549 }
Ken Mixter207694d2010-10-28 15:42:37 -0700550
Ken Mixter777484c2010-07-23 16:22:44 -0700551 return true;
Chris Sosae4a86032010-06-16 17:08:34 -0700552}