| // 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; |
| } |