Index: src/platform/crash/crash_dumper.cc |
diff --git a/src/platform/crash/crash_dumper.cc b/src/platform/crash/crash_dumper.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..94a82636d4f527bd67bac85e804c5b1f3be5c490 |
--- /dev/null |
+++ b/src/platform/crash/crash_dumper.cc |
@@ -0,0 +1,127 @@ |
+// 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. |
+ |
+#include <errno.h> |
+#include <pwd.h> |
+#include <sys/stat.h> |
+#include <sys/types.h> |
+#include <unistd.h> |
+ |
+#include "base/file_path.h" |
+#include "base/file_util.h" |
+#include "base/logging.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 scoped_ptr<google_breakpad::ExceptionHandler> s_breakpad_handler; |
+ |
+static bool s_enabled = false; |
+ |
+ |
+// 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(!s_enabled) << "Crash handling already enabled"; |
+ |
+ std::string name; |
+ CHECK(GetEffectiveUser(&name)) |
+ << "getpwuid_r failed - " << errno |
+ << " - crash reporting cannot be enabled"; |
+ |
+ 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(PrepareCrashPath()) << "Unable to create path " << s_crash_path; |
+ |
+ // Begin collecting crashes |
+ s_breakpad_handler.reset(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(s_enabled) << "Crash handling was not enabled"; |
+ s_breakpad_handler.reset(NULL); |
+ s_crash_path = NULL; |
+ s_crash_parent_path = NULL; |
+ s_enabled = false; |
+} |