user_collector.cc revision c49dbd4775986f32b2f09659595f9f28ef7f6b44
13dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
23dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang// Use of this source code is governed by a BSD-style license that can be
33dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang// found in the LICENSE file.
43dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang
53dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang#include "crash-reporter/user_collector.h"
63dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang
73dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang#include <grp.h>  // For struct group.
83dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang#include <pwd.h>  // For struct passwd.
93dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang#include <sys/types.h>  // For getpwuid_r, getgrnam_r, WEXITSTATUS.
103dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang
113dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang#include <string>
123dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang#include <vector>
133dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang
143dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang#include "base/file_util.h"
153dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang#include "base/logging.h"
163dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang#include "base/string_util.h"
173dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang#include "crash-reporter/system_logging.h"
183dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang#include "gflags/gflags.h"
19b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
20b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang#pragma GCC diagnostic ignored "-Wstrict-aliasing"
21b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik JangDEFINE_bool(core2md_failure_test, false, "Core2md failure test");
22b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik JangDEFINE_bool(directory_failure_test, false, "Spool directory failure test");
23b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik JangDEFINE_string(filter_in, "",
24b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang              "Ignore all crashes but this for testing");
25b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang#pragma GCC diagnostic error "-Wstrict-aliasing"
26b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
27b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jangstatic const char kCollectionErrorSignature[] =
28b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    "crash_reporter-user-collection";
29a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang// This procfs file is used to cause kernel core file writing to
303dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang// instead pipe the core file into a user space process.  See
31a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang// core(5) man page.
32a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jangstatic const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern";
333dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jangstatic const char kCorePipeLimitFile[] = "/proc/sys/kernel/core_pipe_limit";
343dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang// Set core_pipe_limit to 4 so that we can catch a few unrelated concurrent
35b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang// crashes, but finite to avoid infinitely recursing on crash handling.
36b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jangstatic const char kCorePipeLimit[] = "4";
37b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jangstatic const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md";
38b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jangstatic const char kLeaveCoreFile[] = "/root/.leave_core";
39b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
40b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jangstatic const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf";
41b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
42b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jangconst char *UserCollector::kUserId = "Uid:\t";
43b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jangconst char *UserCollector::kGroupId = "Gid:\t";
44b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
453dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik JangUserCollector::UserCollector()
46b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    : generate_diagnostics_(false),
473dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang      core_pattern_file_(kCorePatternFile),
48b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang      core_pipe_limit_file_(kCorePipeLimitFile),
493dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang      initialized_(false) {
503dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang}
51b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
523dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jangvoid UserCollector::Initialize(
533dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang    UserCollector::CountCrashFunction count_crash_function,
543dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang    const std::string &our_path,
553dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang    UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function,
56b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    SystemLogging *logger,
57b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    bool generate_diagnostics) {
583dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang  CrashCollector::Initialize(count_crash_function,
593dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang                             is_feedback_allowed_function,
60b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                             logger);
61b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang  our_path_ = our_path;
62b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang  initialized_ = true;
63b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang  generate_diagnostics_ = generate_diagnostics;
64b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang}
65b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
66b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik JangUserCollector::~UserCollector() {
67b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang}
68b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
69b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jangstd::string UserCollector::GetPattern(bool enabled) const {
70b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang  if (enabled) {
71b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    return StringPrintf("|%s --signal=%%s --pid=%%p", our_path_.c_str());
72b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang  } else {
73b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    return "core";
74b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang  }
75b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang}
76b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
77a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jangbool UserCollector::SetUpInternal(bool enabled) {
78a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang  CHECK(initialized_);
793dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang  logger_->LogInfo("%s user crash handling",
803dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang                   enabled ? "Enabling" : "Disabling");
81b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang  if (file_util::WriteFile(FilePath(core_pipe_limit_file_),
82b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                           kCorePipeLimit,
83b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                           strlen(kCorePipeLimit)) !=
84b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang      static_cast<int>(strlen(kCorePipeLimit))) {
85b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    logger_->LogError("Unable to write %s", core_pipe_limit_file_.c_str());
86b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    return false;
87b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang  }
88b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang  std::string pattern = GetPattern(enabled);
89b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang  if (file_util::WriteFile(FilePath(core_pattern_file_),
903dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang                           pattern.c_str(),
913dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang                           pattern.length()) !=
923dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang      static_cast<int>(pattern.length())) {
93b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    logger_->LogError("Unable to write %s", core_pattern_file_.c_str());
94b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    return false;
95b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang  }
96b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang  return true;
97b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang}
98b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
99b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik JangFilePath UserCollector::GetProcessPath(pid_t pid) {
100b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang  return FilePath(StringPrintf("/proc/%d", pid));
101b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang}
102b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
103b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jangbool UserCollector::GetSymlinkTarget(const FilePath &symlink,
104b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                                     FilePath *target) {
105b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang  int max_size = 32;
106b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang  scoped_array<char> buffer;
107b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang  while (true) {
108b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    buffer.reset(new char[max_size + 1]);
109b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size);
110b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    if (size < 0) {
111b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang      return false;
112b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
113b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    buffer[size] = 0;
114b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    if (size == max_size) {
115b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang      // Avoid overflow when doubling.
116b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang      if (max_size * 2 > max_size) {
117b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        max_size *= 2;
118b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        continue;
119b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang      } else {
120b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        return false;
121b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang      }
1223dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang    }
1233dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang    break;
124b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang  }
125b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
126b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang  *target = FilePath(buffer.get());
127b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang  return true;
128b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang}
129b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
130b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jangbool UserCollector::GetExecutableBaseNameFromPid(uid_t pid,
1313dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang                                                 std::string *base_name) {
1323dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang  FilePath target;
1333dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang  if (!GetSymlinkTarget(GetProcessPath(pid).Append("exe"), &target))
1343dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang    return false;
1353dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang  *base_name = target.BaseName().value();
1363dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang  return true;
137}
138
139bool UserCollector::GetIdFromStatus(const char *prefix,
140                                    IdKind kind,
141                                    const std::string &status_contents,
142                                    int *id) {
143  // From fs/proc/array.c:task_state(), this file contains:
144  // \nUid:\t<uid>\t<euid>\t<suid>\t<fsuid>\n
145  std::vector<std::string> status_lines;
146  SplitString(status_contents, '\n', &status_lines);
147  std::vector<std::string>::iterator line_iterator;
148  for (line_iterator = status_lines.begin();
149       line_iterator != status_lines.end();
150       ++line_iterator) {
151    if (line_iterator->find(prefix) == 0)
152      break;
153  }
154  if (line_iterator == status_lines.end()) {
155    return false;
156  }
157  std::string id_substring = line_iterator->substr(strlen(prefix),
158                                                   std::string::npos);
159  std::vector<std::string> ids;
160  SplitString(id_substring, '\t', &ids);
161  if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) {
162    return false;
163  }
164  const char *number = ids[kind].c_str();
165  char *end_number = NULL;
166  *id = strtol(number, &end_number, 10);
167  if (*end_number != '\0')
168    return false;
169  return true;
170}
171
172void UserCollector::LogCollectionError(const std::string &error_message) {
173  error_log_.append(error_message.c_str());
174  error_log_.append("\n");
175  logger_->LogError(error_message.c_str());
176}
177
178void UserCollector::EnqueueCollectionErrorLog(pid_t pid,
179                                              const std::string &exec) {
180  FilePath crash_path;
181  logger_->LogInfo("Writing conversion problems as separate crash report.");
182  if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, NULL)) {
183    logger_->LogError("Could not even get log directory; out of space?");
184    return;
185  }
186  std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
187  FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
188  FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
189  // We must use WriteNewFile instead of file_util::WriteFile as we do
190  // not want to write with root access to a symlink that an attacker
191  // might have created.
192  WriteNewFile(log_path, error_log_.data(), error_log_.length());
193  AddCrashMetaData("sig", kCollectionErrorSignature);
194  WriteCrashMetaData(meta_path, exec, log_path.value());
195}
196
197bool UserCollector::CopyOffProcFiles(pid_t pid,
198                                     const FilePath &container_dir) {
199  if (!file_util::CreateDirectory(container_dir)) {
200    LogCollectionError(StringPrintf("Could not create %s",
201                                    container_dir.value().c_str()));
202    return false;
203  }
204  FilePath process_path = GetProcessPath(pid);
205  if (!file_util::PathExists(process_path)) {
206    LogCollectionError(StringPrintf("Path %s does not exist",
207                                    process_path.value().c_str()));
208    return false;
209  }
210  static const char *proc_files[] = {
211    "auxv",
212    "cmdline",
213    "environ",
214    "maps",
215    "status"
216  };
217  for (unsigned i = 0; i < arraysize(proc_files); ++i) {
218    if (!file_util::CopyFile(process_path.Append(proc_files[i]),
219                             container_dir.Append(proc_files[i]))) {
220      LogCollectionError(StringPrintf("Could not copy %s file",
221                                      proc_files[i]));
222      return false;
223    }
224  }
225  return true;
226}
227
228bool UserCollector::GetCreatedCrashDirectory(pid_t pid,
229                                             FilePath *crash_file_path,
230                                             bool *out_of_capacity) {
231  FilePath process_path = GetProcessPath(pid);
232  std::string status;
233  if (FLAGS_directory_failure_test) {
234    LogCollectionError("Purposefully failing to create spool directory");
235    return false;
236  }
237  if (!file_util::ReadFileToString(process_path.Append("status"),
238                                   &status)) {
239    LogCollectionError("Could not read status file");
240    return false;
241  }
242  int process_euid;
243  if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) {
244    LogCollectionError("Could not find euid in status file");
245    return false;
246  }
247  if (!GetCreatedCrashDirectoryByEuid(process_euid,
248                                      crash_file_path,
249                                      out_of_capacity)) {
250    LogCollectionError("Could not create crash directory");
251    return false;
252  }
253  return true;
254}
255
256bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) {
257  // Copy off all stdin to a core file.
258  FilePath stdin_path("/dev/fd/0");
259  if (file_util::CopyFile(stdin_path, core_path)) {
260    return true;
261  }
262
263  LogCollectionError("Could not write core file");
264  // If the file system was full, make sure we remove any remnants.
265  file_util::Delete(core_path, false);
266  return false;
267}
268
269bool UserCollector::RunCoreToMinidump(const FilePath &core_path,
270                                      const FilePath &procfs_directory,
271                                      const FilePath &minidump_path,
272                                      const FilePath &temp_directory) {
273  FilePath output_path = temp_directory.Append("output");
274  std::vector<const char *> core2md_arguments;
275  core2md_arguments.push_back(kCoreToMinidumpConverterPath);
276  core2md_arguments.push_back(core_path.value().c_str());
277  core2md_arguments.push_back(procfs_directory.value().c_str());
278  core2md_arguments.push_back(minidump_path.value().c_str());
279
280  if (FLAGS_core2md_failure_test) {
281    // To test how core2md errors are propagaged, cause an error
282    // by forgetting a required argument.
283    core2md_arguments.pop_back();
284  }
285
286  int errorlevel = ForkExecAndPipe(core2md_arguments,
287                                   output_path.value().c_str());
288
289  std::string output;
290  file_util::ReadFileToString(output_path, &output);
291  if (errorlevel != 0) {
292    LogCollectionError(StringPrintf("Problem during %s [result=%d]: %s",
293                                    kCoreToMinidumpConverterPath,
294                                    errorlevel,
295                                    output.c_str()));
296    return false;
297  }
298
299  if (!file_util::PathExists(minidump_path)) {
300    LogCollectionError(StringPrintf("Minidump file %s was not created",
301                                    minidump_path.value().c_str()));
302    return false;
303  }
304  return true;
305}
306
307bool UserCollector::ConvertCoreToMinidump(pid_t pid,
308                                          const FilePath &container_dir,
309                                          const FilePath &core_path,
310                                          const FilePath &minidump_path) {
311  if (!CopyOffProcFiles(pid, container_dir)) {
312    return false;
313  }
314
315  if (!CopyStdinToCoreFile(core_path)) {
316    return false;
317  }
318
319  bool conversion_result = RunCoreToMinidump(
320      core_path,
321      container_dir,  // procfs directory
322      minidump_path,
323      container_dir);  // temporary directory
324
325  if (conversion_result) {
326    logger_->LogInfo("Stored minidump to %s", minidump_path.value().c_str());
327  }
328
329  return conversion_result;
330}
331
332bool UserCollector::ConvertAndEnqueueCrash(int pid,
333                                           const std::string &exec,
334                                           bool *out_of_capacity) {
335  FilePath crash_path;
336  if (!GetCreatedCrashDirectory(pid, &crash_path, out_of_capacity)) {
337    LogCollectionError("Unable to find/create process-specific crash path");
338    return false;
339  }
340
341  // Directory like /tmp/crash_reporter.1234 which contains the
342  // procfs entries and other temporary files used during conversion.
343  FilePath container_dir = FilePath("/tmp").Append(
344      StringPrintf("crash_reporter.%d", pid));
345  std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
346  FilePath core_path = GetCrashPath(crash_path, dump_basename, "core");
347  FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
348  FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp");
349  FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
350
351  if (GetLogContents(FilePath(kDefaultLogConfig), exec, log_path))
352    AddCrashMetaData("log", log_path.value());
353
354  if (!ConvertCoreToMinidump(pid, container_dir, core_path,
355                            minidump_path)) {
356    logger_->LogInfo("Leaving core file at %s due to conversion error",
357                     core_path.value().c_str());
358    return false;
359  }
360
361  // Here we commit to sending this file.  We must not return false
362  // after this point or we will generate a log report as well as a
363  // crash report.
364  WriteCrashMetaData(meta_path,
365                     exec,
366                     minidump_path.value());
367
368  if (!file_util::PathExists(FilePath(kLeaveCoreFile))) {
369    file_util::Delete(core_path, false);
370  } else {
371    logger_->LogInfo("Leaving core file at %s due to developer image",
372                     core_path.value().c_str());
373  }
374
375  file_util::Delete(container_dir, true);
376  return true;
377}
378
379bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) {
380  CHECK(initialized_);
381  std::string exec;
382  if (force_exec) {
383    exec.assign(force_exec);
384  } else if (!GetExecutableBaseNameFromPid(pid, &exec)) {
385    // If for some reason we don't have the base name, avoid completely
386    // failing by indicating an unknown name.
387    exec = "unknown";
388  }
389
390  // Allow us to test the crash reporting mechanism successfully even if
391  // other parts of the system crash.
392  if (!FLAGS_filter_in.empty() &&
393      (FLAGS_filter_in == "none" ||
394       FLAGS_filter_in != exec)) {
395    // We use a different format message to make it more obvious in tests
396    // which crashes are test generated and which are real.
397    logger_->LogWarning("Ignoring crash from %s[%d] while filter_in=%s",
398                        exec.c_str(), pid, FLAGS_filter_in.c_str());
399    return true;
400  }
401
402  bool feedback = is_feedback_allowed_function_();
403  const char *handling_string = "handling";
404  if (!feedback) {
405    handling_string = "ignoring - no consent";
406  }
407
408  // Treat Chrome crashes as if the user opted-out.  We stop counting Chrome
409  // crashes towards user crashes, so user crashes really mean non-Chrome
410  // user-space crashes.
411  if (exec == "chrome") {
412    feedback = false;
413    handling_string = "ignoring - chrome crash";
414  }
415
416  logger_->LogWarning("Received crash notification for %s[%d] sig %d (%s)",
417                      exec.c_str(), pid, signal, handling_string);
418
419  if (feedback) {
420    count_crash_function_();
421
422    if (generate_diagnostics_) {
423      bool out_of_capacity = false;
424      if (!ConvertAndEnqueueCrash(pid, exec, &out_of_capacity)) {
425        if (!out_of_capacity)
426          EnqueueCollectionErrorLog(pid, exec);
427        return false;
428      }
429    }
430  }
431
432  return true;
433}
434