blob: 5ea06e288798f2fb76a9a26eaff3214884ed1dd9 [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
Ken Mixteree849c52010-09-30 15:30:10 -070011#include <set>
12
Ken Mixter03403162010-08-18 15:23:16 -070013#include "base/file_util.h"
14#include "base/logging.h"
15#include "base/string_util.h"
16#include "crash-reporter/system_logging.h"
17
18static const char kDefaultUserName[] = "chronos";
Ken Mixteree849c52010-09-30 15:30:10 -070019static const char kLsbRelease[] = "/etc/lsb-release";
Ken Mixter03403162010-08-18 15:23:16 -070020static const char kSystemCrashPath[] = "/var/spool/crash";
21static const char kUserCrashPath[] = "/home/chronos/user/crash";
22
23// Directory mode of the user crash spool directory.
24static const mode_t kUserCrashPathMode = 0755;
25
26// Directory mode of the system crash spool directory.
27static const mode_t kSystemCrashPathMode = 01755;
28
29static const uid_t kRootOwner = 0;
30static const uid_t kRootGroup = 0;
31
Ken Mixterda5db7a2010-09-17 13:50:42 -070032// Maximum crash reports per crash spool directory. Note that this is
33// a separate maximum from the maximum rate at which we upload these
34// diagnostics. The higher this rate is, the more space we allow for
35// core files, minidumps, and kcrash logs, and equivalently the more
36// processor and I/O bandwidth we dedicate to handling these crashes when
37// many occur at once. Also note that if core files are configured to
38// be left on the file system, we stop adding crashes when either the
39// number of core files or minidumps reaches this number.
40const int CrashCollector::kMaxCrashDirectorySize = 32;
Ken Mixter04ec10f2010-08-26 16:02:02 -070041
Ken Mixterafcf8082010-10-26 14:45:01 -070042CrashCollector::CrashCollector()
43 : forced_crash_directory_(NULL),
44 lsb_release_(kLsbRelease) {
Ken Mixter03403162010-08-18 15:23:16 -070045}
46
47CrashCollector::~CrashCollector() {
48}
49
50void CrashCollector::Initialize(
51 CrashCollector::CountCrashFunction count_crash_function,
52 CrashCollector::IsFeedbackAllowedFunction is_feedback_allowed_function,
53 SystemLogging *logger) {
54 CHECK(count_crash_function != NULL);
55 CHECK(is_feedback_allowed_function != NULL);
56 CHECK(logger != NULL);
57
58 count_crash_function_ = count_crash_function;
59 is_feedback_allowed_function_ = is_feedback_allowed_function;
60 logger_ = logger;
61}
62
Ken Mixteree849c52010-09-30 15:30:10 -070063std::string CrashCollector::Sanitize(const std::string &name) {
64 std::string result = name;
65 for (size_t i = 0; i < name.size(); ++i) {
66 if (!isalnum(result[i]) && result[i] != '_')
67 result[i] = '_';
68 }
69 return result;
70}
71
Ken Mixter03403162010-08-18 15:23:16 -070072std::string CrashCollector::FormatDumpBasename(const std::string &exec_name,
73 time_t timestamp,
74 pid_t pid) {
75 struct tm tm;
76 localtime_r(&timestamp, &tm);
Ken Mixteree849c52010-09-30 15:30:10 -070077 std::string sanitized_exec_name = Sanitize(exec_name);
Ken Mixter03403162010-08-18 15:23:16 -070078 return StringPrintf("%s.%04d%02d%02d.%02d%02d%02d.%d",
Ken Mixteree849c52010-09-30 15:30:10 -070079 sanitized_exec_name.c_str(),
Ken Mixter03403162010-08-18 15:23:16 -070080 tm.tm_year + 1900,
81 tm.tm_mon + 1,
82 tm.tm_mday,
83 tm.tm_hour,
84 tm.tm_min,
85 tm.tm_sec,
86 pid);
87}
88
89FilePath CrashCollector::GetCrashDirectoryInfo(
90 uid_t process_euid,
91 uid_t default_user_id,
92 gid_t default_user_group,
93 mode_t *mode,
94 uid_t *directory_owner,
95 gid_t *directory_group) {
96 if (process_euid == default_user_id) {
97 *mode = kUserCrashPathMode;
98 *directory_owner = default_user_id;
99 *directory_group = default_user_group;
100 return FilePath(kUserCrashPath);
101 } else {
102 *mode = kSystemCrashPathMode;
103 *directory_owner = kRootOwner;
104 *directory_group = kRootGroup;
105 return FilePath(kSystemCrashPath);
106 }
107}
108
109bool CrashCollector::GetUserInfoFromName(const std::string &name,
110 uid_t *uid,
111 gid_t *gid) {
112 char storage[256];
113 struct passwd passwd_storage;
114 struct passwd *passwd_result = NULL;
115
116 if (getpwnam_r(name.c_str(), &passwd_storage, storage, sizeof(storage),
117 &passwd_result) != 0 || passwd_result == NULL) {
118 logger_->LogError("Cannot find user named %s", name.c_str());
119 return false;
120 }
121
122 *uid = passwd_result->pw_uid;
123 *gid = passwd_result->pw_gid;
124 return true;
125}
126
127bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid,
128 FilePath *crash_directory) {
129 uid_t default_user_id;
130 gid_t default_user_group;
131
132 // For testing.
133 if (forced_crash_directory_ != NULL) {
134 *crash_directory = FilePath(forced_crash_directory_);
135 return true;
136 }
137
138 if (!GetUserInfoFromName(kDefaultUserName,
139 &default_user_id,
140 &default_user_group)) {
141 logger_->LogError("Could not find default user info");
142 return false;
143 }
144 mode_t directory_mode;
145 uid_t directory_owner;
146 gid_t directory_group;
147 *crash_directory =
148 GetCrashDirectoryInfo(euid,
149 default_user_id,
150 default_user_group,
151 &directory_mode,
152 &directory_owner,
153 &directory_group);
154
155 if (!file_util::PathExists(*crash_directory)) {
156 // Create the spool directory with the appropriate mode (regardless of
157 // umask) and ownership.
158 mode_t old_mask = umask(0);
159 if (mkdir(crash_directory->value().c_str(), directory_mode) < 0 ||
160 chown(crash_directory->value().c_str(),
161 directory_owner,
162 directory_group) < 0) {
163 logger_->LogError("Unable to create appropriate crash directory");
164 return false;
165 }
166 umask(old_mask);
167 }
168
169 if (!file_util::PathExists(*crash_directory)) {
170 logger_->LogError("Unable to create crash directory %s",
171 crash_directory->value().c_str());
172 return false;
173 }
174
Ken Mixter04ec10f2010-08-26 16:02:02 -0700175 if (!CheckHasCapacity(*crash_directory)) {
176 return false;
177 }
178
Ken Mixter03403162010-08-18 15:23:16 -0700179 return true;
180}
Ken Mixter04ec10f2010-08-26 16:02:02 -0700181
182// Return true if the given crash directory has not already reached
183// maximum capacity.
184bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) {
185 DIR* dir = opendir(crash_directory.value().c_str());
186 if (!dir) {
187 return false;
188 }
189 struct dirent ent_buf;
190 struct dirent* ent;
Ken Mixter04ec10f2010-08-26 16:02:02 -0700191 bool full = false;
Ken Mixteree849c52010-09-30 15:30:10 -0700192 std::set<std::string> basenames;
Ken Mixter04ec10f2010-08-26 16:02:02 -0700193 while (readdir_r(dir, &ent_buf, &ent) == 0 && ent != NULL) {
194 if ((strcmp(ent->d_name, ".") == 0) ||
195 (strcmp(ent->d_name, "..") == 0))
196 continue;
197
Ken Mixteree849c52010-09-30 15:30:10 -0700198 std::string filename(ent->d_name);
199 size_t last_dot = filename.rfind(".");
200 std::string basename;
201 // If there is a valid looking extension, use the base part of the
202 // name. If the only dot is the first byte (aka a dot file), treat
203 // it as unique to avoid allowing a directory full of dot files
204 // from accumulating.
205 if (last_dot != std::string::npos && last_dot != 0)
206 basename = filename.substr(0, last_dot);
207 else
208 basename = filename;
209 basenames.insert(basename);
Ken Mixter04ec10f2010-08-26 16:02:02 -0700210
Ken Mixteree849c52010-09-30 15:30:10 -0700211 if (basenames.size() >= static_cast<size_t>(kMaxCrashDirectorySize)) {
Ken Mixter04ec10f2010-08-26 16:02:02 -0700212 logger_->LogWarning(
213 "Crash directory %s already full with %d pending reports",
214 crash_directory.value().c_str(),
215 kMaxCrashDirectorySize);
216 full = true;
217 break;
218 }
219 }
220 closedir(dir);
221 return !full;
222}
Ken Mixteree849c52010-09-30 15:30:10 -0700223
224bool CrashCollector::ReadKeyValueFile(
225 const FilePath &path,
226 const char separator,
227 std::map<std::string, std::string> *dictionary) {
228 std::string contents;
229 if (!file_util::ReadFileToString(path, &contents)) {
230 return false;
231 }
232 typedef std::vector<std::string> StringVector;
233 StringVector lines;
234 SplitString(contents, '\n', &lines);
235 bool any_errors = false;
236 for (StringVector::iterator line = lines.begin(); line != lines.end();
237 ++line) {
238 // Allow empty strings.
239 if (line->empty())
240 continue;
241 StringVector sides;
242 SplitString(*line, separator, &sides);
243 if (sides.size() != 2) {
244 any_errors = true;
245 continue;
246 }
247 dictionary->insert(std::pair<std::string, std::string>(sides[0], sides[1]));
248 }
249 return !any_errors;
250}
251
Ken Mixterafcf8082010-10-26 14:45:01 -0700252void CrashCollector::AddCrashMetaData(const std::string &key,
253 const std::string &value) {
254 extra_metadata_.append(StringPrintf("%s=%s\n", key.c_str(), value.c_str()));
255}
256
Ken Mixteree849c52010-09-30 15:30:10 -0700257void CrashCollector::WriteCrashMetaData(const FilePath &meta_path,
Ken Mixterc909b692010-10-18 12:26:05 -0700258 const std::string &exec_name,
259 const std::string &payload_path) {
Ken Mixteree849c52010-09-30 15:30:10 -0700260 std::map<std::string, std::string> contents;
Ken Mixterafcf8082010-10-26 14:45:01 -0700261 if (!ReadKeyValueFile(FilePath(std::string(lsb_release_)), '=', &contents)) {
262 logger_->LogError("Problem parsing %s", lsb_release_);
Ken Mixteree849c52010-09-30 15:30:10 -0700263 // Even though there was some failure, take as much as we could read.
264 }
265 std::string version("unknown");
266 std::map<std::string, std::string>::iterator i;
267 if ((i = contents.find("CHROMEOS_RELEASE_VERSION")) != contents.end()) {
268 version = i->second;
269 }
Ken Mixterc909b692010-10-18 12:26:05 -0700270 int64 payload_size = -1;
271 file_util::GetFileSize(FilePath(payload_path), &payload_size);
Ken Mixterafcf8082010-10-26 14:45:01 -0700272 std::string meta_data = StringPrintf("%sexec_name=%s\n"
Ken Mixteree849c52010-09-30 15:30:10 -0700273 "ver=%s\n"
Ken Mixterc909b692010-10-18 12:26:05 -0700274 "payload_size=%lld\n"
Ken Mixteree849c52010-09-30 15:30:10 -0700275 "done=1\n",
Ken Mixterafcf8082010-10-26 14:45:01 -0700276 extra_metadata_.c_str(),
Ken Mixteree849c52010-09-30 15:30:10 -0700277 exec_name.c_str(),
Ken Mixterc909b692010-10-18 12:26:05 -0700278 version.c_str(),
279 payload_size);
Ken Mixteree849c52010-09-30 15:30:10 -0700280 if (!file_util::WriteFile(meta_path, meta_data.c_str(), meta_data.size())) {
281 logger_->LogError("Unable to write %s", meta_path.value().c_str());
282 }
283}