blob: 1e2cd1abdd66e016993540fecc3979328badad72 [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
Ken Mixter207694d2010-10-28 15:42:37 -070089FilePath CrashCollector::GetCrashPath(const FilePath &crash_directory,
90 const std::string &basename,
91 const std::string &extension) {
92 return crash_directory.Append(StringPrintf("%s.%s",
93 basename.c_str(),
94 extension.c_str()));
95}
96
Ken Mixter03403162010-08-18 15:23:16 -070097FilePath CrashCollector::GetCrashDirectoryInfo(
98 uid_t process_euid,
99 uid_t default_user_id,
100 gid_t default_user_group,
101 mode_t *mode,
102 uid_t *directory_owner,
103 gid_t *directory_group) {
104 if (process_euid == default_user_id) {
105 *mode = kUserCrashPathMode;
106 *directory_owner = default_user_id;
107 *directory_group = default_user_group;
108 return FilePath(kUserCrashPath);
109 } else {
110 *mode = kSystemCrashPathMode;
111 *directory_owner = kRootOwner;
112 *directory_group = kRootGroup;
113 return FilePath(kSystemCrashPath);
114 }
115}
116
117bool CrashCollector::GetUserInfoFromName(const std::string &name,
118 uid_t *uid,
119 gid_t *gid) {
120 char storage[256];
121 struct passwd passwd_storage;
122 struct passwd *passwd_result = NULL;
123
124 if (getpwnam_r(name.c_str(), &passwd_storage, storage, sizeof(storage),
125 &passwd_result) != 0 || passwd_result == NULL) {
126 logger_->LogError("Cannot find user named %s", name.c_str());
127 return false;
128 }
129
130 *uid = passwd_result->pw_uid;
131 *gid = passwd_result->pw_gid;
132 return true;
133}
134
135bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid,
Ken Mixter207694d2010-10-28 15:42:37 -0700136 FilePath *crash_directory,
137 bool *out_of_capacity) {
Ken Mixter03403162010-08-18 15:23:16 -0700138 uid_t default_user_id;
139 gid_t default_user_group;
140
Ken Mixter207694d2010-10-28 15:42:37 -0700141 if (out_of_capacity != NULL) *out_of_capacity = false;
142
Ken Mixter03403162010-08-18 15:23:16 -0700143 // For testing.
144 if (forced_crash_directory_ != NULL) {
145 *crash_directory = FilePath(forced_crash_directory_);
146 return true;
147 }
148
149 if (!GetUserInfoFromName(kDefaultUserName,
150 &default_user_id,
151 &default_user_group)) {
152 logger_->LogError("Could not find default user info");
153 return false;
154 }
155 mode_t directory_mode;
156 uid_t directory_owner;
157 gid_t directory_group;
158 *crash_directory =
159 GetCrashDirectoryInfo(euid,
160 default_user_id,
161 default_user_group,
162 &directory_mode,
163 &directory_owner,
164 &directory_group);
165
166 if (!file_util::PathExists(*crash_directory)) {
167 // Create the spool directory with the appropriate mode (regardless of
168 // umask) and ownership.
169 mode_t old_mask = umask(0);
170 if (mkdir(crash_directory->value().c_str(), directory_mode) < 0 ||
171 chown(crash_directory->value().c_str(),
172 directory_owner,
173 directory_group) < 0) {
174 logger_->LogError("Unable to create appropriate crash directory");
175 return false;
176 }
177 umask(old_mask);
178 }
179
180 if (!file_util::PathExists(*crash_directory)) {
181 logger_->LogError("Unable to create crash directory %s",
182 crash_directory->value().c_str());
183 return false;
184 }
185
Ken Mixter04ec10f2010-08-26 16:02:02 -0700186 if (!CheckHasCapacity(*crash_directory)) {
Ken Mixter207694d2010-10-28 15:42:37 -0700187 if (out_of_capacity != NULL) *out_of_capacity = true;
Ken Mixter04ec10f2010-08-26 16:02:02 -0700188 return false;
189 }
190
Ken Mixter03403162010-08-18 15:23:16 -0700191 return true;
192}
Ken Mixter04ec10f2010-08-26 16:02:02 -0700193
194// Return true if the given crash directory has not already reached
195// maximum capacity.
196bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) {
197 DIR* dir = opendir(crash_directory.value().c_str());
198 if (!dir) {
199 return false;
200 }
201 struct dirent ent_buf;
202 struct dirent* ent;
Ken Mixter04ec10f2010-08-26 16:02:02 -0700203 bool full = false;
Ken Mixteree849c52010-09-30 15:30:10 -0700204 std::set<std::string> basenames;
Ken Mixter04ec10f2010-08-26 16:02:02 -0700205 while (readdir_r(dir, &ent_buf, &ent) == 0 && ent != NULL) {
206 if ((strcmp(ent->d_name, ".") == 0) ||
207 (strcmp(ent->d_name, "..") == 0))
208 continue;
209
Ken Mixteree849c52010-09-30 15:30:10 -0700210 std::string filename(ent->d_name);
211 size_t last_dot = filename.rfind(".");
212 std::string basename;
213 // If there is a valid looking extension, use the base part of the
214 // name. If the only dot is the first byte (aka a dot file), treat
215 // it as unique to avoid allowing a directory full of dot files
216 // from accumulating.
217 if (last_dot != std::string::npos && last_dot != 0)
218 basename = filename.substr(0, last_dot);
219 else
220 basename = filename;
221 basenames.insert(basename);
Ken Mixter04ec10f2010-08-26 16:02:02 -0700222
Ken Mixteree849c52010-09-30 15:30:10 -0700223 if (basenames.size() >= static_cast<size_t>(kMaxCrashDirectorySize)) {
Ken Mixter04ec10f2010-08-26 16:02:02 -0700224 logger_->LogWarning(
225 "Crash directory %s already full with %d pending reports",
226 crash_directory.value().c_str(),
227 kMaxCrashDirectorySize);
228 full = true;
229 break;
230 }
231 }
232 closedir(dir);
233 return !full;
234}
Ken Mixteree849c52010-09-30 15:30:10 -0700235
236bool CrashCollector::ReadKeyValueFile(
237 const FilePath &path,
238 const char separator,
239 std::map<std::string, std::string> *dictionary) {
240 std::string contents;
241 if (!file_util::ReadFileToString(path, &contents)) {
242 return false;
243 }
244 typedef std::vector<std::string> StringVector;
245 StringVector lines;
246 SplitString(contents, '\n', &lines);
247 bool any_errors = false;
248 for (StringVector::iterator line = lines.begin(); line != lines.end();
249 ++line) {
250 // Allow empty strings.
251 if (line->empty())
252 continue;
253 StringVector sides;
254 SplitString(*line, separator, &sides);
255 if (sides.size() != 2) {
256 any_errors = true;
257 continue;
258 }
259 dictionary->insert(std::pair<std::string, std::string>(sides[0], sides[1]));
260 }
261 return !any_errors;
262}
263
Ken Mixterafcf8082010-10-26 14:45:01 -0700264void CrashCollector::AddCrashMetaData(const std::string &key,
265 const std::string &value) {
266 extra_metadata_.append(StringPrintf("%s=%s\n", key.c_str(), value.c_str()));
267}
268
Ken Mixteree849c52010-09-30 15:30:10 -0700269void CrashCollector::WriteCrashMetaData(const FilePath &meta_path,
Ken Mixterc909b692010-10-18 12:26:05 -0700270 const std::string &exec_name,
271 const std::string &payload_path) {
Ken Mixteree849c52010-09-30 15:30:10 -0700272 std::map<std::string, std::string> contents;
Ken Mixterafcf8082010-10-26 14:45:01 -0700273 if (!ReadKeyValueFile(FilePath(std::string(lsb_release_)), '=', &contents)) {
274 logger_->LogError("Problem parsing %s", lsb_release_);
Ken Mixteree849c52010-09-30 15:30:10 -0700275 // Even though there was some failure, take as much as we could read.
276 }
277 std::string version("unknown");
278 std::map<std::string, std::string>::iterator i;
279 if ((i = contents.find("CHROMEOS_RELEASE_VERSION")) != contents.end()) {
280 version = i->second;
281 }
Ken Mixterc909b692010-10-18 12:26:05 -0700282 int64 payload_size = -1;
283 file_util::GetFileSize(FilePath(payload_path), &payload_size);
Ken Mixterafcf8082010-10-26 14:45:01 -0700284 std::string meta_data = StringPrintf("%sexec_name=%s\n"
Ken Mixteree849c52010-09-30 15:30:10 -0700285 "ver=%s\n"
Ken Mixter207694d2010-10-28 15:42:37 -0700286 "payload=%s\n"
Ken Mixterc909b692010-10-18 12:26:05 -0700287 "payload_size=%lld\n"
Ken Mixteree849c52010-09-30 15:30:10 -0700288 "done=1\n",
Ken Mixterafcf8082010-10-26 14:45:01 -0700289 extra_metadata_.c_str(),
Ken Mixteree849c52010-09-30 15:30:10 -0700290 exec_name.c_str(),
Ken Mixterc909b692010-10-18 12:26:05 -0700291 version.c_str(),
Ken Mixter207694d2010-10-28 15:42:37 -0700292 payload_path.c_str(),
Ken Mixterc909b692010-10-18 12:26:05 -0700293 payload_size);
Ken Mixteree849c52010-09-30 15:30:10 -0700294 if (!file_util::WriteFile(meta_path, meta_data.c_str(), meta_data.size())) {
295 logger_->LogError("Unable to write %s", meta_path.value().c_str());
296 }
297}