blob: f2f514694180b750240f4ea197b1673b99e66d48 [file] [log] [blame]
Ken Mixter03403162010-08-18 15:23:16 -07001// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "crash-reporter/crash_collector.h"
6
Ken Mixter04ec10f2010-08-26 16:02:02 -07007#include <dirent.h>
Ken Mixter03403162010-08-18 15:23:16 -07008#include <pwd.h> // For struct passwd.
9#include <sys/types.h> // for mode_t.
10
11#include "base/file_util.h"
12#include "base/logging.h"
13#include "base/string_util.h"
14#include "crash-reporter/system_logging.h"
15
16static const char kDefaultUserName[] = "chronos";
17static const char kSystemCrashPath[] = "/var/spool/crash";
18static const char kUserCrashPath[] = "/home/chronos/user/crash";
19
20// Directory mode of the user crash spool directory.
21static const mode_t kUserCrashPathMode = 0755;
22
23// Directory mode of the system crash spool directory.
24static const mode_t kSystemCrashPathMode = 01755;
25
26static const uid_t kRootOwner = 0;
27static const uid_t kRootGroup = 0;
28
Ken Mixterda5db7a2010-09-17 13:50:42 -070029// Maximum crash reports per crash spool directory. Note that this is
30// a separate maximum from the maximum rate at which we upload these
31// diagnostics. The higher this rate is, the more space we allow for
32// core files, minidumps, and kcrash logs, and equivalently the more
33// processor and I/O bandwidth we dedicate to handling these crashes when
34// many occur at once. Also note that if core files are configured to
35// be left on the file system, we stop adding crashes when either the
36// number of core files or minidumps reaches this number.
37const int CrashCollector::kMaxCrashDirectorySize = 32;
Ken Mixter04ec10f2010-08-26 16:02:02 -070038
Ken Mixter03403162010-08-18 15:23:16 -070039CrashCollector::CrashCollector() : forced_crash_directory_(NULL) {
40}
41
42CrashCollector::~CrashCollector() {
43}
44
45void CrashCollector::Initialize(
46 CrashCollector::CountCrashFunction count_crash_function,
47 CrashCollector::IsFeedbackAllowedFunction is_feedback_allowed_function,
48 SystemLogging *logger) {
49 CHECK(count_crash_function != NULL);
50 CHECK(is_feedback_allowed_function != NULL);
51 CHECK(logger != NULL);
52
53 count_crash_function_ = count_crash_function;
54 is_feedback_allowed_function_ = is_feedback_allowed_function;
55 logger_ = logger;
56}
57
58std::string CrashCollector::FormatDumpBasename(const std::string &exec_name,
59 time_t timestamp,
60 pid_t pid) {
61 struct tm tm;
62 localtime_r(&timestamp, &tm);
63 return StringPrintf("%s.%04d%02d%02d.%02d%02d%02d.%d",
64 exec_name.c_str(),
65 tm.tm_year + 1900,
66 tm.tm_mon + 1,
67 tm.tm_mday,
68 tm.tm_hour,
69 tm.tm_min,
70 tm.tm_sec,
71 pid);
72}
73
74FilePath CrashCollector::GetCrashDirectoryInfo(
75 uid_t process_euid,
76 uid_t default_user_id,
77 gid_t default_user_group,
78 mode_t *mode,
79 uid_t *directory_owner,
80 gid_t *directory_group) {
81 if (process_euid == default_user_id) {
82 *mode = kUserCrashPathMode;
83 *directory_owner = default_user_id;
84 *directory_group = default_user_group;
85 return FilePath(kUserCrashPath);
86 } else {
87 *mode = kSystemCrashPathMode;
88 *directory_owner = kRootOwner;
89 *directory_group = kRootGroup;
90 return FilePath(kSystemCrashPath);
91 }
92}
93
94bool CrashCollector::GetUserInfoFromName(const std::string &name,
95 uid_t *uid,
96 gid_t *gid) {
97 char storage[256];
98 struct passwd passwd_storage;
99 struct passwd *passwd_result = NULL;
100
101 if (getpwnam_r(name.c_str(), &passwd_storage, storage, sizeof(storage),
102 &passwd_result) != 0 || passwd_result == NULL) {
103 logger_->LogError("Cannot find user named %s", name.c_str());
104 return false;
105 }
106
107 *uid = passwd_result->pw_uid;
108 *gid = passwd_result->pw_gid;
109 return true;
110}
111
112bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid,
113 FilePath *crash_directory) {
114 uid_t default_user_id;
115 gid_t default_user_group;
116
117 // For testing.
118 if (forced_crash_directory_ != NULL) {
119 *crash_directory = FilePath(forced_crash_directory_);
120 return true;
121 }
122
123 if (!GetUserInfoFromName(kDefaultUserName,
124 &default_user_id,
125 &default_user_group)) {
126 logger_->LogError("Could not find default user info");
127 return false;
128 }
129 mode_t directory_mode;
130 uid_t directory_owner;
131 gid_t directory_group;
132 *crash_directory =
133 GetCrashDirectoryInfo(euid,
134 default_user_id,
135 default_user_group,
136 &directory_mode,
137 &directory_owner,
138 &directory_group);
139
140 if (!file_util::PathExists(*crash_directory)) {
141 // Create the spool directory with the appropriate mode (regardless of
142 // umask) and ownership.
143 mode_t old_mask = umask(0);
144 if (mkdir(crash_directory->value().c_str(), directory_mode) < 0 ||
145 chown(crash_directory->value().c_str(),
146 directory_owner,
147 directory_group) < 0) {
148 logger_->LogError("Unable to create appropriate crash directory");
149 return false;
150 }
151 umask(old_mask);
152 }
153
154 if (!file_util::PathExists(*crash_directory)) {
155 logger_->LogError("Unable to create crash directory %s",
156 crash_directory->value().c_str());
157 return false;
158 }
159
Ken Mixter04ec10f2010-08-26 16:02:02 -0700160 if (!CheckHasCapacity(*crash_directory)) {
161 return false;
162 }
163
Ken Mixter03403162010-08-18 15:23:16 -0700164 return true;
165}
Ken Mixter04ec10f2010-08-26 16:02:02 -0700166
167// Return true if the given crash directory has not already reached
168// maximum capacity.
169bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) {
170 DIR* dir = opendir(crash_directory.value().c_str());
171 if (!dir) {
172 return false;
173 }
174 struct dirent ent_buf;
175 struct dirent* ent;
176 int count_non_core = 0;
177 int count_core = 0;
178 bool full = false;
179 while (readdir_r(dir, &ent_buf, &ent) == 0 && ent != NULL) {
180 if ((strcmp(ent->d_name, ".") == 0) ||
181 (strcmp(ent->d_name, "..") == 0))
182 continue;
183
184 if (strcmp(ent->d_name + strlen(ent->d_name) - 5, ".core") == 0) {
185 ++count_core;
186 } else {
187 ++count_non_core;
188 }
189
190 if (count_core >= kMaxCrashDirectorySize ||
191 count_non_core >= kMaxCrashDirectorySize) {
192 logger_->LogWarning(
193 "Crash directory %s already full with %d pending reports",
194 crash_directory.value().c_str(),
195 kMaxCrashDirectorySize);
196 full = true;
197 break;
198 }
199 }
200 closedir(dir);
201 return !full;
202}