user_collector.cc revision 74dc62460b8cdd5bfeac47bfe8e759fc04b55ef8
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 "user_collector.h"
18
19#include <elf.h>
20#include <fcntl.h>
21#include <grp.h>  // For struct group.
22#include <pcrecpp.h>
23#include <pwd.h>  // For struct passwd.
24#include <stdint.h>
25#include <sys/cdefs.h>  // For __WORDSIZE
26#include <sys/fsuid.h>
27#include <sys/types.h>  // For getpwuid_r, getgrnam_r, WEXITSTATUS.
28#include <unistd.h>  // For setgroups
29
30#include <iostream>  // For std::oct
31#include <string>
32#include <vector>
33
34#include <base/files/file_util.h>
35#include <base/logging.h>
36#include <base/posix/eintr_wrapper.h>
37#include <base/strings/string_split.h>
38#include <base/strings/string_util.h>
39#include <base/strings/stringprintf.h>
40#include <brillo/osrelease_reader.h>
41#include <brillo/process.h>
42#include <brillo/syslog_logging.h>
43#include <cutils/properties.h>
44#include <private/android_filesystem_config.h>
45
46static const char kCollectionErrorSignature[] =
47    "crash_reporter-user-collection";
48static const char kCorePatternProperty[] = "crash_reporter.coredump.enabled";
49static const char kCoreToMinidumpConverterPath[] = "/system/bin/core2md";
50
51static const char kStatePrefix[] = "State:\t";
52
53static const char kCoreTempFolder[] = "/data/misc/crash_reporter/tmp";
54
55// Define an otherwise invalid value that represents an unknown UID and GID.
56static const uid_t kUnknownUid = -1;
57static const gid_t kUnknownGid = -1;
58
59const char *UserCollector::kUserId = "Uid:\t";
60const char *UserCollector::kGroupId = "Gid:\t";
61
62// The property containing the OS version.
63const char kVersionProperty[] = "ro.build.id";
64
65// The property containing the product id.
66const char kProductIDProperty[] = "ro.product.product_id";
67
68static const char kBdkVersionKey[] = "bdk_version";
69
70
71using base::FilePath;
72using base::StringPrintf;
73
74UserCollector::UserCollector()
75    : generate_diagnostics_(false),
76      initialized_(false) {
77}
78
79void UserCollector::Initialize(
80    UserCollector::CountCrashFunction count_crash_function,
81    const std::string &our_path,
82    UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function,
83    bool generate_diagnostics,
84    bool core2md_failure,
85    bool directory_failure,
86    const std::string &filter_in) {
87  CrashCollector::Initialize(count_crash_function,
88                             is_feedback_allowed_function);
89  our_path_ = our_path;
90  initialized_ = true;
91  generate_diagnostics_ = generate_diagnostics;
92  core2md_failure_ = core2md_failure;
93  directory_failure_ = directory_failure;
94  filter_in_ = filter_in;
95
96  gid_t groups[] = { AID_ROOT, AID_SYSTEM, AID_DBUS };
97  if (setgroups(arraysize(groups), groups) != 0) {
98    PLOG(FATAL) << "Unable to set groups to root, system, and dbus";
99  }
100}
101
102UserCollector::~UserCollector() {
103}
104
105std::string UserCollector::GetErrorTypeSignature(ErrorType error_type) const {
106  switch (error_type) {
107    case kErrorSystemIssue:
108      return "system-issue";
109    case kErrorReadCoreData:
110      return "read-core-data";
111    case kErrorUnusableProcFiles:
112      return "unusable-proc-files";
113    case kErrorInvalidCoreFile:
114      return "invalid-core-file";
115    case kErrorUnsupported32BitCoreFile:
116      return "unsupported-32bit-core-file";
117    case kErrorCore2MinidumpConversion:
118      return "core2md-conversion";
119    default:
120      return "";
121  }
122}
123
124bool UserCollector::SetUpInternal(bool enabled) {
125  CHECK(initialized_);
126  LOG(INFO) << (enabled ? "Enabling" : "Disabling") << " user crash handling";
127
128  property_set(kCorePatternProperty, enabled ? "1" : "0");
129
130  return true;
131}
132
133bool UserCollector::GetFirstLineWithPrefix(
134    const std::vector<std::string> &lines,
135    const char *prefix, std::string *line) {
136  std::vector<std::string>::const_iterator line_iterator;
137  for (line_iterator = lines.begin(); line_iterator != lines.end();
138       ++line_iterator) {
139    if (line_iterator->find(prefix) == 0) {
140      *line = *line_iterator;
141      return true;
142    }
143  }
144  return false;
145}
146
147bool UserCollector::GetIdFromStatus(
148    const char *prefix, IdKind kind,
149    const std::vector<std::string> &status_lines, int *id) {
150  // From fs/proc/array.c:task_state(), this file contains:
151  // \nUid:\t<uid>\t<euid>\t<suid>\t<fsuid>\n
152  std::string id_line;
153  if (!GetFirstLineWithPrefix(status_lines, prefix, &id_line)) {
154    return false;
155  }
156  std::string id_substring = id_line.substr(strlen(prefix), std::string::npos);
157  std::vector<std::string> ids;
158  base::SplitString(id_substring, '\t', &ids);
159  if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) {
160    return false;
161  }
162  const char *number = ids[kind].c_str();
163  char *end_number = nullptr;
164  *id = strtol(number, &end_number, 10);
165  if (*end_number != '\0') {
166    return false;
167  }
168  return true;
169}
170
171bool UserCollector::GetStateFromStatus(
172    const std::vector<std::string> &status_lines, std::string *state) {
173  std::string state_line;
174  if (!GetFirstLineWithPrefix(status_lines, kStatePrefix, &state_line)) {
175    return false;
176  }
177  *state = state_line.substr(strlen(kStatePrefix), std::string::npos);
178  return true;
179}
180
181void UserCollector::EnqueueCollectionErrorLog(pid_t pid,
182                                              ErrorType error_type,
183                                              const std::string &exec) {
184  FilePath crash_path;
185  LOG(INFO) << "Writing conversion problems as separate crash report.";
186  if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, nullptr)) {
187    LOG(ERROR) << "Could not even get log directory; out of space?";
188    return;
189  }
190  AddCrashMetaData("sig", kCollectionErrorSignature);
191  AddCrashMetaData("error_type", GetErrorTypeSignature(error_type));
192  std::string dump_basename = FormatDumpBasename(exec, time(nullptr), pid);
193  std::string error_log = brillo::GetLog();
194  FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog");
195  if (GetLogContents(FilePath(log_config_path_), kCollectionErrorSignature,
196                     diag_log_path)) {
197    // We load the contents of diag_log into memory and append it to
198    // the error log.  We cannot just append to files because we need
199    // to always create new files to prevent attack.
200    std::string diag_log_contents;
201    base::ReadFileToString(diag_log_path, &diag_log_contents);
202    error_log.append(diag_log_contents);
203    base::DeleteFile(diag_log_path, false);
204  }
205  FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
206  FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
207  // We must use WriteNewFile instead of base::WriteFile as we do
208  // not want to write with root access to a symlink that an attacker
209  // might have created.
210  if (WriteNewFile(log_path, error_log.data(), error_log.length()) < 0) {
211    LOG(ERROR) << "Error writing new file " << log_path.value();
212    return;
213  }
214  WriteCrashMetaData(meta_path, exec, log_path.value());
215}
216
217bool UserCollector::CopyOffProcFiles(pid_t pid,
218                                     const FilePath &container_dir) {
219  if (!base::CreateDirectory(container_dir)) {
220    PLOG(ERROR) << "Could not create " << container_dir.value();
221    return false;
222  }
223  int dir_mask = base::FILE_PERMISSION_READ_BY_USER
224                 | base::FILE_PERMISSION_WRITE_BY_USER
225                 | base::FILE_PERMISSION_EXECUTE_BY_USER
226                 | base::FILE_PERMISSION_READ_BY_GROUP
227                 | base::FILE_PERMISSION_WRITE_BY_GROUP;
228  if (!base::SetPosixFilePermissions(container_dir,
229                                     base::FILE_PERMISSION_MASK & dir_mask)) {
230    PLOG(ERROR) << "Could not set permissions for " << container_dir.value()
231                << " to " << std::oct
232                << (base::FILE_PERMISSION_MASK & dir_mask);
233    return false;
234  }
235  FilePath process_path = GetProcessPath(pid);
236  if (!base::PathExists(process_path)) {
237    LOG(ERROR) << "Path " << process_path.value() << " does not exist";
238    return false;
239  }
240  static const char *proc_files[] = {
241    "auxv",
242    "cmdline",
243    "environ",
244    "maps",
245    "status"
246  };
247  for (unsigned i = 0; i < arraysize(proc_files); ++i) {
248    if (!base::CopyFile(process_path.Append(proc_files[i]),
249                        container_dir.Append(proc_files[i]))) {
250      LOG(ERROR) << "Could not copy " << proc_files[i] << " file";
251      return false;
252    }
253  }
254  return true;
255}
256
257bool UserCollector::ValidateProcFiles(const FilePath &container_dir) const {
258  // Check if the maps file is empty, which could be due to the crashed
259  // process being reaped by the kernel before finishing a core dump.
260  int64_t file_size = 0;
261  if (!base::GetFileSize(container_dir.Append("maps"), &file_size)) {
262    LOG(ERROR) << "Could not get the size of maps file";
263    return false;
264  }
265  if (file_size == 0) {
266    LOG(ERROR) << "maps file is empty";
267    return false;
268  }
269  return true;
270}
271
272UserCollector::ErrorType UserCollector::ValidateCoreFile(
273    const FilePath &core_path) const {
274  int fd = HANDLE_EINTR(open(core_path.value().c_str(), O_RDONLY));
275  if (fd < 0) {
276    PLOG(ERROR) << "Could not open core file " << core_path.value();
277    return kErrorInvalidCoreFile;
278  }
279
280  char e_ident[EI_NIDENT];
281  bool read_ok = base::ReadFromFD(fd, e_ident, sizeof(e_ident));
282  IGNORE_EINTR(close(fd));
283  if (!read_ok) {
284    LOG(ERROR) << "Could not read header of core file";
285    return kErrorInvalidCoreFile;
286  }
287
288  if (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 ||
289      e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) {
290    LOG(ERROR) << "Invalid core file";
291    return kErrorInvalidCoreFile;
292  }
293
294#if __WORDSIZE == 64
295  // TODO(benchan, mkrebs): Remove this check once core2md can
296  // handles both 32-bit and 64-bit ELF on a 64-bit platform.
297  if (e_ident[EI_CLASS] == ELFCLASS32) {
298    LOG(ERROR) << "Conversion of 32-bit core file on 64-bit platform is "
299               << "currently not supported";
300    return kErrorUnsupported32BitCoreFile;
301  }
302#endif
303
304  return kErrorNone;
305}
306
307bool UserCollector::GetCreatedCrashDirectory(pid_t pid, uid_t supplied_ruid,
308                                             FilePath *crash_file_path,
309                                             bool *out_of_capacity) {
310  FilePath process_path = GetProcessPath(pid);
311  std::string status;
312  if (directory_failure_) {
313    LOG(ERROR) << "Purposefully failing to create spool directory";
314    return false;
315  }
316
317  uid_t uid;
318  if (base::ReadFileToString(process_path.Append("status"), &status)) {
319    std::vector<std::string> status_lines;
320    base::SplitString(status, '\n', &status_lines);
321
322    std::string process_state;
323    if (!GetStateFromStatus(status_lines, &process_state)) {
324      LOG(ERROR) << "Could not find process state in status file";
325      return false;
326    }
327    LOG(INFO) << "State of crashed process [" << pid << "]: " << process_state;
328
329    // Get effective UID of crashing process.
330    int id;
331    if (!GetIdFromStatus(kUserId, kIdEffective, status_lines, &id)) {
332      LOG(ERROR) << "Could not find euid in status file";
333      return false;
334    }
335    uid = id;
336  } else if (supplied_ruid != kUnknownUid) {
337    LOG(INFO) << "Using supplied UID " << supplied_ruid
338              << " for crashed process [" << pid
339              << "] due to error reading status file";
340    uid = supplied_ruid;
341  } else {
342    LOG(ERROR) << "Could not read status file and kernel did not supply UID";
343    LOG(INFO) << "Path " << process_path.value() << " DirectoryExists: "
344              << base::DirectoryExists(process_path);
345    return false;
346  }
347
348  if (!GetCreatedCrashDirectoryByEuid(uid, crash_file_path, out_of_capacity)) {
349    LOG(ERROR) << "Could not create crash directory";
350    return false;
351  }
352  return true;
353}
354
355bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) {
356  // Copy off all stdin to a core file.
357  FilePath stdin_path("/proc/self/fd/0");
358  if (base::CopyFile(stdin_path, core_path)) {
359    return true;
360  }
361
362  PLOG(ERROR) << "Could not write core file";
363  // If the file system was full, make sure we remove any remnants.
364  base::DeleteFile(core_path, false);
365  return false;
366}
367
368bool UserCollector::RunCoreToMinidump(const FilePath &core_path,
369                                      const FilePath &procfs_directory,
370                                      const FilePath &minidump_path,
371                                      const FilePath &temp_directory) {
372  FilePath output_path = temp_directory.Append("output");
373  brillo::ProcessImpl core2md;
374  core2md.RedirectOutput(output_path.value());
375  core2md.AddArg(kCoreToMinidumpConverterPath);
376  core2md.AddArg(core_path.value());
377  core2md.AddArg(procfs_directory.value());
378
379  if (!core2md_failure_) {
380    core2md.AddArg(minidump_path.value());
381  } else {
382    // To test how core2md errors are propagaged, cause an error
383    // by forgetting a required argument.
384  }
385
386  int errorlevel = core2md.Run();
387
388  std::string output;
389  base::ReadFileToString(output_path, &output);
390  if (errorlevel != 0) {
391    LOG(ERROR) << "Problem during " << kCoreToMinidumpConverterPath
392               << " [result=" << errorlevel << "]: " << output;
393    return false;
394  }
395
396  if (!base::PathExists(minidump_path)) {
397    LOG(ERROR) << "Minidump file " << minidump_path.value()
398               << " was not created";
399    return false;
400  }
401  return true;
402}
403
404UserCollector::ErrorType UserCollector::ConvertCoreToMinidump(
405    pid_t pid,
406    const FilePath &container_dir,
407    const FilePath &core_path,
408    const FilePath &minidump_path) {
409  // If proc files are unuable, we continue to read the core file from stdin,
410  // but only skip the core-to-minidump conversion, so that we may still use
411  // the core file for debugging.
412  bool proc_files_usable =
413      CopyOffProcFiles(pid, container_dir) && ValidateProcFiles(container_dir);
414
415  // Switch back to the original UID/GID.
416  gid_t rgid, egid, sgid;
417  if (getresgid(&rgid, &egid, &sgid) != 0) {
418    PLOG(FATAL) << "Unable to read saved gid";
419  }
420  if (setresgid(sgid, sgid, -1) != 0) {
421    PLOG(FATAL) << "Unable to set real group ID back to saved gid";
422  } else {
423    if (getresgid(&rgid, &egid, &sgid) != 0) {
424      // If the groups cannot be read at this point, the rgid variable will
425      // contain the previously read group ID from before changing it.  This
426      // will cause the chown call below to set the incorrect group for
427      // non-root crashes.  But do not treat this as a fatal error, so that
428      // the rest of the collection will continue for potential manual
429      // collection by a developer.
430      PLOG(ERROR) << "Unable to read real group ID after setting it";
431    }
432  }
433
434  uid_t ruid, euid, suid;
435  if (getresuid(&ruid, &euid, &suid) != 0) {
436    PLOG(FATAL) << "Unable to read saved uid";
437  }
438  if (setresuid(suid, suid, -1) != 0) {
439    PLOG(FATAL) << "Unable to set real user ID back to saved uid";
440  } else {
441    if (getresuid(&ruid, &euid, &suid) != 0) {
442      // If the user ID cannot be read at this point, the ruid variable will
443      // contain the previously read user ID from before changing it.  This
444      // will cause the chown call below to set the incorrect user for
445      // non-root crashes.  But do not treat this as a fatal error, so that
446      // the rest of the collection will continue for potential manual
447      // collection by a developer.
448      PLOG(ERROR) << "Unable to read real user ID after setting it";
449    }
450  }
451
452  if (!CopyStdinToCoreFile(core_path)) {
453    return kErrorReadCoreData;
454  }
455
456  if (!proc_files_usable) {
457    LOG(INFO) << "Skipped converting core file to minidump due to "
458              << "unusable proc files";
459    return kErrorUnusableProcFiles;
460  }
461
462  ErrorType error = ValidateCoreFile(core_path);
463  if (error != kErrorNone) {
464    return error;
465  }
466
467  // Chown the temp container directory back to the original user/group that
468  // crash_reporter is run as, so that additional files can be written to
469  // the temp folder.
470  if (chown(container_dir.value().c_str(), ruid, rgid) < 0) {
471    PLOG(ERROR) << "Could not set owner for " << container_dir.value();
472  }
473
474  if (!RunCoreToMinidump(core_path,
475                         container_dir,  // procfs directory
476                         minidump_path,
477                         container_dir)) {  // temporary directory
478    return kErrorCore2MinidumpConversion;
479  }
480
481  LOG(INFO) << "Stored minidump to " << minidump_path.value();
482  return kErrorNone;
483}
484
485UserCollector::ErrorType UserCollector::ConvertAndEnqueueCrash(
486    pid_t pid, const std::string &exec, uid_t supplied_ruid,
487    bool *out_of_capacity) {
488  FilePath crash_path;
489  if (!GetCreatedCrashDirectory(pid, supplied_ruid, &crash_path,
490      out_of_capacity)) {
491    LOG(ERROR) << "Unable to find/create process-specific crash path";
492    return kErrorSystemIssue;
493  }
494
495  // Directory like /tmp/crash_reporter/1234 which contains the
496  // procfs entries and other temporary files used during conversion.
497  FilePath container_dir(StringPrintf("%s/%d", kCoreTempFolder, pid));
498  // Delete a pre-existing directory from crash reporter that may have
499  // been left around for diagnostics from a failed conversion attempt.
500  // If we don't, existing files can cause forking to fail.
501  base::DeleteFile(container_dir, true);
502  std::string dump_basename = FormatDumpBasename(exec, time(nullptr), pid);
503  FilePath core_path = GetCrashPath(crash_path, dump_basename, "core");
504  FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
505  FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp");
506  FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
507
508  if (GetLogContents(FilePath(log_config_path_), exec, log_path))
509    AddCrashMetaData("log", log_path.value());
510
511  char value[PROPERTY_VALUE_MAX];
512  property_get(kVersionProperty, value, "undefined");
513  AddCrashMetaUploadData("ver", value);
514  property_get(kProductIDProperty, value, "undefined");
515  AddCrashMetaUploadData("prod", value);
516
517  brillo::OsReleaseReader reader;
518  reader.Load();
519  std::string bdk_version = "undefined";
520  if (!reader.GetString(kBdkVersionKey, &bdk_version)) {
521    LOG(ERROR) << "Could not read " << kBdkVersionKey
522               << " from /etc/os-release.d/";
523  }
524  AddCrashMetaData(kBdkVersionKey, bdk_version);
525
526  ErrorType error_type =
527      ConvertCoreToMinidump(pid, container_dir, core_path, minidump_path);
528  if (error_type != kErrorNone) {
529    LOG(INFO) << "Leaving core file at " << core_path.value()
530              << " due to conversion error";
531    return error_type;
532  }
533
534  // Here we commit to sending this file.  We must not return false
535  // after this point or we will generate a log report as well as a
536  // crash report.
537  WriteCrashMetaData(meta_path,
538                     exec,
539                     minidump_path.value());
540
541  if (!IsDeveloperImage()) {
542    base::DeleteFile(core_path, false);
543  } else {
544    LOG(INFO) << "Leaving core file at " << core_path.value()
545              << " due to developer image";
546  }
547
548  base::DeleteFile(container_dir, true);
549  return kErrorNone;
550}
551
552bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes,
553                                         pid_t *pid, int *signal, uid_t *uid,
554                                         gid_t *gid,
555                                         std::string *kernel_supplied_name) {
556  pcrecpp::RE re("(\\d+):(\\d+):(\\d+):(\\d+):(.*)");
557  if (re.FullMatch(crash_attributes, pid, signal, uid, gid,
558                   kernel_supplied_name))
559    return true;
560
561  LOG(INFO) << "Falling back to parsing crash attributes '"
562            << crash_attributes << "' without UID and GID";
563  pcrecpp::RE re_without_uid("(\\d+):(\\d+):(.*)");
564  *uid = kUnknownUid;
565  *gid = kUnknownGid;
566  return re_without_uid.FullMatch(crash_attributes, pid, signal,
567      kernel_supplied_name);
568}
569
570bool UserCollector::ShouldDump(bool has_owner_consent,
571                               bool is_developer,
572                               std::string *reason) {
573  reason->clear();
574
575  // For developer builds, we always want to keep the crash reports unless
576  // we're testing the crash facilities themselves.  This overrides
577  // feedback.  Crash sending still obeys consent.
578  if (is_developer) {
579    *reason = "developer build - not testing - always dumping";
580    return true;
581  }
582
583  if (!has_owner_consent) {
584    *reason = "ignoring - no consent";
585    return false;
586  }
587
588  *reason = "handling";
589  return true;
590}
591
592bool UserCollector::HandleCrash(const std::string &crash_attributes,
593                                const char *force_exec) {
594  CHECK(initialized_);
595  pid_t pid = 0;
596  int signal = 0;
597  uid_t supplied_ruid = kUnknownUid;
598  gid_t supplied_rgid = kUnknownGid;
599  std::string kernel_supplied_name;
600
601  if (!ParseCrashAttributes(crash_attributes, &pid, &signal, &supplied_ruid,
602                            &supplied_rgid, &kernel_supplied_name)) {
603    LOG(ERROR) << "Invalid parameter: --user=" <<  crash_attributes;
604    return false;
605  }
606
607  // Switch to the group and user that ran the crashing binary in order to
608  // access their /proc files.  Do not set suid/sgid, so that we can switch
609  // back after copying the necessary files.
610  if (setresgid(supplied_rgid, supplied_rgid, -1) != 0) {
611    PLOG(FATAL) << "Unable to set real group ID to access process files";
612  }
613  if (setresuid(supplied_ruid, supplied_ruid, -1) != 0) {
614    PLOG(FATAL) << "Unable to set real user ID to access process files";
615  }
616
617  std::string exec;
618  if (force_exec) {
619    exec.assign(force_exec);
620  } else if (!GetExecutableBaseNameFromPid(pid, &exec)) {
621    // If we cannot find the exec name, use the kernel supplied name.
622    // We don't always use the kernel's since it truncates the name to
623    // 16 characters.
624    exec = StringPrintf("supplied_%s", kernel_supplied_name.c_str());
625  }
626
627  // Allow us to test the crash reporting mechanism successfully even if
628  // other parts of the system crash.
629  if (!filter_in_.empty() &&
630      (filter_in_ == "none" ||
631       filter_in_ != exec)) {
632    // We use a different format message to make it more obvious in tests
633    // which crashes are test generated and which are real.
634    LOG(WARNING) << "Ignoring crash from " << exec << "[" << pid << "] while "
635                 << "filter_in=" << filter_in_ << ".";
636    return true;
637  }
638
639  std::string reason;
640  bool dump = ShouldDump(is_feedback_allowed_function_(),
641                         IsDeveloperImage(),
642                         &reason);
643
644  LOG(WARNING) << "Received crash notification for " << exec << "[" << pid
645               << "] sig " << signal << ", user " << supplied_ruid
646               << " (" << reason << ")";
647
648  if (dump) {
649    count_crash_function_();
650
651    if (generate_diagnostics_) {
652      bool out_of_capacity = false;
653      ErrorType error_type =
654          ConvertAndEnqueueCrash(pid, exec, supplied_ruid, &out_of_capacity);
655      if (error_type != kErrorNone) {
656        if (!out_of_capacity)
657          EnqueueCollectionErrorLog(pid, error_type, exec);
658        return false;
659      }
660    }
661  }
662
663  return true;
664}
665