| Index: user_collector.cc
 | 
| diff --git a/user_collector.cc b/user_collector.cc
 | 
| index 59b460bdca6ae261cfa8bfe2890170a5fc91c2f5..a6dd091e770349587bc052a924fe790ed372cf8f 100644
 | 
| --- a/user_collector.cc
 | 
| +++ b/user_collector.cc
 | 
| @@ -5,6 +5,8 @@
 | 
|  #include "crash-reporter/user_collector.h"
 | 
|  
 | 
|  #include <grp.h>  // For struct group.
 | 
| +#include <pcrecpp.h>
 | 
| +#include <pcrecpp.h>
 | 
|  #include <pwd.h>  // For struct passwd.
 | 
|  #include <sys/types.h>  // For getpwuid_r, getgrnam_r, WEXITSTATUS.
 | 
|  
 | 
| @@ -18,8 +20,8 @@
 | 
|  #include "gflags/gflags.h"
 | 
|  
 | 
|  #pragma GCC diagnostic ignored "-Wstrict-aliasing"
 | 
| -DEFINE_bool(core2md_failure_test, false, "Core2md failure test");
 | 
| -DEFINE_bool(directory_failure_test, false, "Spool directory failure test");
 | 
| +DEFINE_bool(core2md_failure, false, "Core2md failure test");
 | 
| +DEFINE_bool(directory_failure, false, "Spool directory failure test");
 | 
|  DEFINE_string(filter_in, "",
 | 
|                "Ignore all crashes but this for testing");
 | 
|  #pragma GCC diagnostic error "-Wstrict-aliasing"
 | 
| @@ -68,7 +70,11 @@ UserCollector::~UserCollector() {
 | 
|  
 | 
|  std::string UserCollector::GetPattern(bool enabled) const {
 | 
|    if (enabled) {
 | 
| -    return StringPrintf("|%s --signal=%%s --pid=%%p", our_path_.c_str());
 | 
| +    // Combine the three crash attributes into one parameter to try to reduce
 | 
| +    // the size of the invocation line for crash_reporter since the kernel
 | 
| +    // has a fixed-sized (128B) buffer that it will truncate into.  Note that
 | 
| +    // the kernel does not support quoted arguments in core_pattern.
 | 
| +    return StringPrintf("|%s --user=%%p:%%s:%%e", our_path_.c_str());
 | 
|    } else {
 | 
|      return "core";
 | 
|    }
 | 
| @@ -169,12 +175,6 @@ bool UserCollector::GetIdFromStatus(const char *prefix,
 | 
|    return true;
 | 
|  }
 | 
|  
 | 
| -void UserCollector::LogCollectionError(const std::string &error_message) {
 | 
| -  error_log_.append(error_message.c_str());
 | 
| -  error_log_.append("\n");
 | 
| -  logger_->LogError(error_message.c_str());
 | 
| -}
 | 
| -
 | 
|  void UserCollector::EnqueueCollectionErrorLog(pid_t pid,
 | 
|                                                const std::string &exec) {
 | 
|    FilePath crash_path;
 | 
| @@ -184,12 +184,24 @@ void UserCollector::EnqueueCollectionErrorLog(pid_t pid,
 | 
|      return;
 | 
|    }
 | 
|    std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
 | 
| +  std::string error_log = logger_->get_accumulator();
 | 
| +  FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog");
 | 
| +  if (GetLogContents(FilePath(kDefaultLogConfig), kCollectionErrorSignature,
 | 
| +                     diag_log_path)) {
 | 
| +    // We load the contents of diag_log into memory and append it to
 | 
| +    // the error log.  We cannot just append to files because we need
 | 
| +    // to always create new files to prevent attack.
 | 
| +    std::string diag_log_contents;
 | 
| +    file_util::ReadFileToString(diag_log_path, &diag_log_contents);
 | 
| +    error_log.append(diag_log_contents);
 | 
| +    file_util::Delete(diag_log_path, false);
 | 
| +  }
 | 
|    FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
 | 
|    FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
 | 
|    // We must use WriteNewFile instead of file_util::WriteFile as we do
 | 
|    // not want to write with root access to a symlink that an attacker
 | 
|    // might have created.
 | 
| -  WriteNewFile(log_path, error_log_.data(), error_log_.length());
 | 
| +  WriteNewFile(log_path, error_log.data(), error_log.length());
 | 
|    AddCrashMetaData("sig", kCollectionErrorSignature);
 | 
|    WriteCrashMetaData(meta_path, exec, log_path.value());
 | 
|  }
 | 
| @@ -197,14 +209,13 @@ void UserCollector::EnqueueCollectionErrorLog(pid_t pid,
 | 
|  bool UserCollector::CopyOffProcFiles(pid_t pid,
 | 
|                                       const FilePath &container_dir) {
 | 
|    if (!file_util::CreateDirectory(container_dir)) {
 | 
| -    LogCollectionError(StringPrintf("Could not create %s",
 | 
| -                                    container_dir.value().c_str()));
 | 
| +    logger_->LogError("Could not create %s",
 | 
| +                      container_dir.value().c_str());
 | 
|      return false;
 | 
|    }
 | 
|    FilePath process_path = GetProcessPath(pid);
 | 
|    if (!file_util::PathExists(process_path)) {
 | 
| -    LogCollectionError(StringPrintf("Path %s does not exist",
 | 
| -                                    process_path.value().c_str()));
 | 
| +    logger_->LogError("Path %s does not exist", process_path.value().c_str());
 | 
|      return false;
 | 
|    }
 | 
|    static const char *proc_files[] = {
 | 
| @@ -217,8 +228,7 @@ bool UserCollector::CopyOffProcFiles(pid_t pid,
 | 
|    for (unsigned i = 0; i < arraysize(proc_files); ++i) {
 | 
|      if (!file_util::CopyFile(process_path.Append(proc_files[i]),
 | 
|                               container_dir.Append(proc_files[i]))) {
 | 
| -      LogCollectionError(StringPrintf("Could not copy %s file",
 | 
| -                                      proc_files[i]));
 | 
| +      logger_->LogError("Could not copy %s file", proc_files[i]);
 | 
|        return false;
 | 
|      }
 | 
|    }
 | 
| @@ -230,24 +240,27 @@ bool UserCollector::GetCreatedCrashDirectory(pid_t pid,
 | 
|                                               bool *out_of_capacity) {
 | 
|    FilePath process_path = GetProcessPath(pid);
 | 
|    std::string status;
 | 
| -  if (FLAGS_directory_failure_test) {
 | 
| -    LogCollectionError("Purposefully failing to create spool directory");
 | 
| +  if (FLAGS_directory_failure) {
 | 
| +    logger_->LogError("Purposefully failing to create spool directory");
 | 
|      return false;
 | 
|    }
 | 
|    if (!file_util::ReadFileToString(process_path.Append("status"),
 | 
|                                     &status)) {
 | 
| -    LogCollectionError("Could not read status file");
 | 
| +    logger_->LogError("Could not read status file");
 | 
| +    logger_->LogInfo("Path %s FileExists: %d",
 | 
| +                     process_path.value().c_str(),
 | 
| +                     file_util::DirectoryExists(process_path));
 | 
|      return false;
 | 
|    }
 | 
|    int process_euid;
 | 
|    if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) {
 | 
| -    LogCollectionError("Could not find euid in status file");
 | 
| +    logger_->LogError("Could not find euid in status file");
 | 
|      return false;
 | 
|    }
 | 
|    if (!GetCreatedCrashDirectoryByEuid(process_euid,
 | 
|                                        crash_file_path,
 | 
|                                        out_of_capacity)) {
 | 
| -    LogCollectionError("Could not create crash directory");
 | 
| +    logger_->LogError("Could not create crash directory");
 | 
|      return false;
 | 
|    }
 | 
|    return true;
 | 
| @@ -260,7 +273,7 @@ bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) {
 | 
|      return true;
 | 
|    }
 | 
|  
 | 
| -  LogCollectionError("Could not write core file");
 | 
| +  logger_->LogError("Could not write core file");
 | 
|    // If the file system was full, make sure we remove any remnants.
 | 
|    file_util::Delete(core_path, false);
 | 
|    return false;
 | 
| @@ -277,7 +290,7 @@ bool UserCollector::RunCoreToMinidump(const FilePath &core_path,
 | 
|    core2md_arguments.push_back(procfs_directory.value().c_str());
 | 
|    core2md_arguments.push_back(minidump_path.value().c_str());
 | 
|  
 | 
| -  if (FLAGS_core2md_failure_test) {
 | 
| +  if (FLAGS_core2md_failure) {
 | 
|      // To test how core2md errors are propagaged, cause an error
 | 
|      // by forgetting a required argument.
 | 
|      core2md_arguments.pop_back();
 | 
| @@ -289,16 +302,16 @@ bool UserCollector::RunCoreToMinidump(const FilePath &core_path,
 | 
|    std::string output;
 | 
|    file_util::ReadFileToString(output_path, &output);
 | 
|    if (errorlevel != 0) {
 | 
| -    LogCollectionError(StringPrintf("Problem during %s [result=%d]: %s",
 | 
| -                                    kCoreToMinidumpConverterPath,
 | 
| -                                    errorlevel,
 | 
| -                                    output.c_str()));
 | 
| +    logger_->LogError("Problem during %s [result=%d]: %s",
 | 
| +                      kCoreToMinidumpConverterPath,
 | 
| +                      errorlevel,
 | 
| +                      output.c_str());
 | 
|      return false;
 | 
|    }
 | 
|  
 | 
|    if (!file_util::PathExists(minidump_path)) {
 | 
| -    LogCollectionError(StringPrintf("Minidump file %s was not created",
 | 
| -                                    minidump_path.value().c_str()));
 | 
| +    logger_->LogError("Minidump file %s was not created",
 | 
| +                      minidump_path.value().c_str());
 | 
|      return false;
 | 
|    }
 | 
|    return true;
 | 
| @@ -334,7 +347,7 @@ bool UserCollector::ConvertAndEnqueueCrash(int pid,
 | 
|                                             bool *out_of_capacity) {
 | 
|    FilePath crash_path;
 | 
|    if (!GetCreatedCrashDirectory(pid, &crash_path, out_of_capacity)) {
 | 
| -    LogCollectionError("Unable to find/create process-specific crash path");
 | 
| +    logger_->LogError("Unable to find/create process-specific crash path");
 | 
|      return false;
 | 
|    }
 | 
|  
 | 
| @@ -342,6 +355,10 @@ bool UserCollector::ConvertAndEnqueueCrash(int pid,
 | 
|    // procfs entries and other temporary files used during conversion.
 | 
|    FilePath container_dir = FilePath("/tmp").Append(
 | 
|        StringPrintf("crash_reporter.%d", pid));
 | 
| +  // Delete a pre-existing directory from crash reporter that may have
 | 
| +  // been left around for diagnostics from a failed conversion attempt.
 | 
| +  // If we don't, existing files can cause forking to fail.
 | 
| +  file_util::Delete(container_dir, true);
 | 
|    std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
 | 
|    FilePath core_path = GetCrashPath(crash_path, dump_basename, "core");
 | 
|    FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
 | 
| @@ -376,15 +393,34 @@ bool UserCollector::ConvertAndEnqueueCrash(int pid,
 | 
|    return true;
 | 
|  }
 | 
|  
 | 
| -bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) {
 | 
| +bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes,
 | 
| +                                         pid_t *pid, int *signal,
 | 
| +                                         std::string *kernel_supplied_name) {
 | 
| +  pcrecpp::RE re("(\\d+):(\\d+):(.*)");
 | 
| +  return re.FullMatch(crash_attributes, pid, signal, kernel_supplied_name);
 | 
| +}
 | 
| +
 | 
| +bool UserCollector::HandleCrash(const std::string &crash_attributes,
 | 
| +                                const char *force_exec) {
 | 
|    CHECK(initialized_);
 | 
| +  int pid = 0;
 | 
| +  int signal = 0;
 | 
| +  std::string kernel_supplied_name;
 | 
| +
 | 
| +  if (!ParseCrashAttributes(crash_attributes, &pid, &signal,
 | 
| +                            &kernel_supplied_name)) {
 | 
| +    logger_->LogError("Invalid parameter: --user=%s", crash_attributes.c_str());
 | 
| +    return false;
 | 
| +  }
 | 
| +
 | 
|    std::string exec;
 | 
|    if (force_exec) {
 | 
|      exec.assign(force_exec);
 | 
|    } else if (!GetExecutableBaseNameFromPid(pid, &exec)) {
 | 
| -    // If for some reason we don't have the base name, avoid completely
 | 
| -    // failing by indicating an unknown name.
 | 
| -    exec = "unknown";
 | 
| +    // If we cannot find the exec name, use the kernel supplied name.
 | 
| +    // We don't always use the kernel's since it truncates the name to
 | 
| +    // 16 characters.
 | 
| +    exec = StringPrintf("supplied_%s", kernel_supplied_name.c_str());
 | 
|    }
 | 
|  
 | 
|    // Allow us to test the crash reporting mechanism successfully even if
 | 
| @@ -394,7 +430,7 @@ bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) {
 | 
|         FLAGS_filter_in != exec)) {
 | 
|      // We use a different format message to make it more obvious in tests
 | 
|      // which crashes are test generated and which are real.
 | 
| -    logger_->LogWarning("Ignoring crash from %s[%d] while filter_in=%s",
 | 
| +    logger_->LogWarning("Ignoring crash from %s[%d] while filter_in=%s.",
 | 
|                          exec.c_str(), pid, FLAGS_filter_in.c_str());
 | 
|      return true;
 | 
|    }
 | 
| @@ -421,7 +457,11 @@ bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) {
 | 
|  
 | 
|      if (generate_diagnostics_) {
 | 
|        bool out_of_capacity = false;
 | 
| -      if (!ConvertAndEnqueueCrash(pid, exec, &out_of_capacity)) {
 | 
| +      logger_->set_accumulating(true);
 | 
| +      bool convert_and_enqueue_result =
 | 
| +          ConvertAndEnqueueCrash(pid, exec, &out_of_capacity);
 | 
| +      logger_->set_accumulating(false);
 | 
| +      if (!convert_and_enqueue_result) {
 | 
|          if (!out_of_capacity)
 | 
|            EnqueueCollectionErrorLog(pid, exec);
 | 
|          return false;
 | 
| 
 |