blob: 52d18d7425d1d538db30bd3b5f157c6e71f0eb67 [file] [log] [blame]
// Copyright (c) 2010 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.
//
// Simple wrapper and basic configuration of Google breakpad. We try
// to avoid using any unnecessary code (like libbase) to make it as
// non-intrusive as possible to link in this library.
#include <errno.h>
#include <pwd.h>
#include <stddef.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "client/linux/handler/exception_handler.h"
#include "common/linux/linux_libc_support.h"
#include "common/linux/linux_syscall_support.h"
#include "crash/crash_dumper.h"
// Define sys_mkdir (sys_open passes mode_t as int, so we do too).
LSS_INLINE _syscall2(int, mkdir, const char *, pathname, int, mode);
static const char kDefaultUser[] = "chronos";
static const char kSystemCrashParentPath[] = "/var/spool";
static const char kSystemCrashPath[] = "/var/spool/crash";
static const char kUserCrashParentPath[] = "/home/chronos/user";
static const char kUserCrashPath[] = "/home/chronos/user/crash";
// Pointers to paths, set when crash dumping is enabled.
static const char *s_crash_path;
static const char *s_crash_parent_path;
static mode_t s_dump_directory_mode;
static bool s_any_crashes_occurred = false;
static google_breakpad::ExceptionHandler *s_breakpad_handler;
static bool s_enabled = false;
#define CHECK_CONDITION(_cond, _message) \
do { if (!(_cond)) { fputs(_message"\n", stderr); abort(); } } while(false)
// This static object will cause anything that links in this object to get
// crash handling for the duration of this file's scope.
static CrashDumper g_crash_dumper;
// Prepare the crash path. Must avoid allocating memory.
static bool PrepareCrashPath() {
struct kernel_stat buf;
if (sys_stat(s_crash_path, &buf) < 0) {
// Dump directory does not exist, so create it and its parent now,
// at the time of the crash.
sys_mkdir(s_crash_parent_path, 755);
sys_mkdir(s_crash_path, s_dump_directory_mode);
}
return sys_stat(s_crash_path, &buf) == 0;
}
// Use FilterCallback to avoid recursive crashing.
// TODO(kmixter): Also use it to avoid enqueuing too many crash dumps
// system wide - if we get in a crash/restart loop we don't want the entire
// stateful partition to be filled up.
static bool FilterCallback(void *) {
// This function runs in a compromised context - a crash has already
// occurred so memory allocation and libc should be avoided.
bool old_any_crashes_occured = s_any_crashes_occurred;
s_any_crashes_occurred = true;
// The crash path may have been removed or mounted-over, so make sure
// there is a container directory. If it fails, not much we can do.
PrepareCrashPath();
return !old_any_crashes_occured;
}
static bool GetEffectiveUser(std::string *result) {
char storage[256];
struct passwd passwd_storage;
struct passwd *passwd_result = NULL;
if (getpwuid_r(geteuid(), &passwd_storage, storage, sizeof(storage),
&passwd_result) != 0 || passwd_result == NULL) {
return false;
}
*result = passwd_result->pw_name;
return true;
}
void CrashDumper::Enable() {
CHECK_CONDITION(!s_enabled, "Crash handling already enabled");
std::string name;
CHECK_CONDITION(GetEffectiveUser(&name),
"getpwuid_r failed, cannot enable crash reporting");
if (name == kDefaultUser) {
// Crashes as "chronos" when the user is not yet logged in will
// still be recorded to /home/chronos/user/crash in the
// stateful partition (outside cryptohome). These will eventually
// be uploaded when no user is logged in.
s_crash_path = kUserCrashPath;
s_crash_parent_path = kUserCrashParentPath;
s_dump_directory_mode = 0755;
} else {
s_crash_path = kSystemCrashPath;
s_crash_parent_path = kSystemCrashParentPath;
// Make the dump directory sticky so any UID can write to
// it but not remove another UID's crashes.
s_dump_directory_mode = 01777;
}
CHECK_CONDITION(PrepareCrashPath(), "Unable to create crash path");
// Begin collecting crashes
s_breakpad_handler = new google_breakpad::ExceptionHandler(
s_crash_path,
FilterCallback,
NULL, // No minidump callback - sending happens asynchronously to writing
NULL, // No callback context necessary
true); // Install handler now.
s_enabled = true;
}
bool CrashDumper::IsEnabled() {
return s_enabled;
}
void CrashDumper::Disable() {
CHECK_CONDITION(s_enabled, "Crash handling was not enabled");
delete s_breakpad_handler;
s_breakpad_handler = NULL;
s_crash_path = NULL;
s_crash_parent_path = NULL;
s_enabled = false;
}