blob: 85c0933fc35afd0e72700ba95c3f30bc0ef9b967 [file] [log] [blame]
Simon Quef70060c2012-04-09 19:07:07 -07001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Ken Mixter03403162010-08-18 15:23:16 -07002// 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 Mixter9b346472010-11-07 13:45:45 -08008#include <fcntl.h> // For file creation modes.
Ken Mixter03403162010-08-18 15:23:16 -07009#include <pwd.h> // For struct passwd.
10#include <sys/types.h> // for mode_t.
Ken Mixter9b346472010-11-07 13:45:45 -080011#include <sys/wait.h> // For waitpid.
12#include <unistd.h> // For execv and fork.
Mike Frysinger65b4c1e2011-09-21 12:41:29 -040013#define __STDC_FORMAT_MACROS // PRId64
14#include <inttypes.h>
Ken Mixter03403162010-08-18 15:23:16 -070015
Ken Mixteree849c52010-09-30 15:30:10 -070016#include <set>
Simon Quef70060c2012-04-09 19:07:07 -070017#include <vector>
Ken Mixteree849c52010-09-30 15:30:10 -070018
Ken Mixter03403162010-08-18 15:23:16 -070019#include "base/file_util.h"
20#include "base/logging.h"
Mike Frysinger1a8780d2013-02-14 22:39:57 -050021#include "base/posix/eintr_wrapper.h"
Chris Masone8a68c7c2011-05-14 11:44:04 -070022#include "base/string_split.h"
Ken Mixter03403162010-08-18 15:23:16 -070023#include "base/string_util.h"
Mike Frysinger57b261c2012-04-11 14:47:09 -040024#include "base/stringprintf.h"
Ken Mixtera3249322011-03-03 08:47:38 -080025#include "chromeos/process.h"
Ken Mixter03403162010-08-18 15:23:16 -070026
Michael Krebs4fe30db2011-08-05 13:54:52 -070027static const char kCollectChromeFile[] =
28 "/mnt/stateful_partition/etc/collect_chrome_crashes";
29static const char kCrashTestInProgressPath[] = "/tmp/crash-test-in-progress";
Simon Quef70060c2012-04-09 19:07:07 -070030static const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf";
Ken Mixter03403162010-08-18 15:23:16 -070031static const char kDefaultUserName[] = "chronos";
Michael Krebs4fe30db2011-08-05 13:54:52 -070032static const char kLeaveCoreFile[] = "/root/.leave_core";
Ken Mixteree849c52010-09-30 15:30:10 -070033static const char kLsbRelease[] = "/etc/lsb-release";
Ken Mixterc49dbd42010-12-14 17:44:11 -080034static const char kShellPath[] = "/bin/sh";
Ken Mixter03403162010-08-18 15:23:16 -070035static const char kSystemCrashPath[] = "/var/spool/crash";
36static const char kUserCrashPath[] = "/home/chronos/user/crash";
37
38// Directory mode of the user crash spool directory.
39static const mode_t kUserCrashPathMode = 0755;
40
41// Directory mode of the system crash spool directory.
42static const mode_t kSystemCrashPathMode = 01755;
43
44static const uid_t kRootOwner = 0;
45static const uid_t kRootGroup = 0;
46
Ken Mixterda5db7a2010-09-17 13:50:42 -070047// Maximum crash reports per crash spool directory. Note that this is
48// a separate maximum from the maximum rate at which we upload these
49// diagnostics. The higher this rate is, the more space we allow for
50// core files, minidumps, and kcrash logs, and equivalently the more
51// processor and I/O bandwidth we dedicate to handling these crashes when
52// many occur at once. Also note that if core files are configured to
53// be left on the file system, we stop adding crashes when either the
54// number of core files or minidumps reaches this number.
55const int CrashCollector::kMaxCrashDirectorySize = 32;
Ken Mixter04ec10f2010-08-26 16:02:02 -070056
Simon Que9f90aca2013-02-19 17:19:52 -080057using base::FilePath;
58
Ken Mixterafcf8082010-10-26 14:45:01 -070059CrashCollector::CrashCollector()
60 : forced_crash_directory_(NULL),
Simon Queacc79382012-05-04 18:10:09 -070061 lsb_release_(kLsbRelease),
62 log_config_path_(kDefaultLogConfig) {
Ken Mixter03403162010-08-18 15:23:16 -070063}
64
65CrashCollector::~CrashCollector() {
66}
67
68void CrashCollector::Initialize(
69 CrashCollector::CountCrashFunction count_crash_function,
Ken Mixtera3249322011-03-03 08:47:38 -080070 CrashCollector::IsFeedbackAllowedFunction is_feedback_allowed_function) {
Ken Mixter03403162010-08-18 15:23:16 -070071 CHECK(count_crash_function != NULL);
72 CHECK(is_feedback_allowed_function != NULL);
Ken Mixter03403162010-08-18 15:23:16 -070073
74 count_crash_function_ = count_crash_function;
75 is_feedback_allowed_function_ = is_feedback_allowed_function;
Ken Mixter03403162010-08-18 15:23:16 -070076}
77
Ken Mixter9b346472010-11-07 13:45:45 -080078int CrashCollector::WriteNewFile(const FilePath &filename,
79 const char *data,
80 int size) {
81 int fd = HANDLE_EINTR(open(filename.value().c_str(),
82 O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0666));
83 if (fd < 0) {
84 return -1;
85 }
86
87 int rv = file_util::WriteFileDescriptor(fd, data, size);
88 HANDLE_EINTR(close(fd));
89 return rv;
90}
91
Ken Mixteree849c52010-09-30 15:30:10 -070092std::string CrashCollector::Sanitize(const std::string &name) {
93 std::string result = name;
94 for (size_t i = 0; i < name.size(); ++i) {
95 if (!isalnum(result[i]) && result[i] != '_')
96 result[i] = '_';
97 }
98 return result;
99}
100
Ken Mixter03403162010-08-18 15:23:16 -0700101std::string CrashCollector::FormatDumpBasename(const std::string &exec_name,
102 time_t timestamp,
103 pid_t pid) {
104 struct tm tm;
105 localtime_r(&timestamp, &tm);
Ken Mixteree849c52010-09-30 15:30:10 -0700106 std::string sanitized_exec_name = Sanitize(exec_name);
Ken Mixter03403162010-08-18 15:23:16 -0700107 return StringPrintf("%s.%04d%02d%02d.%02d%02d%02d.%d",
Ken Mixteree849c52010-09-30 15:30:10 -0700108 sanitized_exec_name.c_str(),
Ken Mixter03403162010-08-18 15:23:16 -0700109 tm.tm_year + 1900,
110 tm.tm_mon + 1,
111 tm.tm_mday,
112 tm.tm_hour,
113 tm.tm_min,
114 tm.tm_sec,
115 pid);
116}
117
Ken Mixter207694d2010-10-28 15:42:37 -0700118FilePath CrashCollector::GetCrashPath(const FilePath &crash_directory,
119 const std::string &basename,
120 const std::string &extension) {
121 return crash_directory.Append(StringPrintf("%s.%s",
122 basename.c_str(),
123 extension.c_str()));
124}
125
Ken Mixter03403162010-08-18 15:23:16 -0700126FilePath CrashCollector::GetCrashDirectoryInfo(
127 uid_t process_euid,
128 uid_t default_user_id,
129 gid_t default_user_group,
130 mode_t *mode,
131 uid_t *directory_owner,
132 gid_t *directory_group) {
Michael Krebs4fe30db2011-08-05 13:54:52 -0700133 // TODO(mkrebs): This can go away once Chrome crashes are handled
134 // normally (see crosbug.com/5872).
135 // Check if the user crash directory should be used. If we are
136 // collecting chrome crashes during autotesting, we want to put them in
137 // the system crash directory so they are outside the cryptohome -- in
138 // case we are being run during logout (see crosbug.com/18637).
139 if (process_euid == default_user_id && IsUserSpecificDirectoryEnabled()) {
Ken Mixter03403162010-08-18 15:23:16 -0700140 *mode = kUserCrashPathMode;
141 *directory_owner = default_user_id;
142 *directory_group = default_user_group;
143 return FilePath(kUserCrashPath);
144 } else {
145 *mode = kSystemCrashPathMode;
146 *directory_owner = kRootOwner;
147 *directory_group = kRootGroup;
148 return FilePath(kSystemCrashPath);
149 }
150}
151
152bool CrashCollector::GetUserInfoFromName(const std::string &name,
153 uid_t *uid,
154 gid_t *gid) {
155 char storage[256];
156 struct passwd passwd_storage;
157 struct passwd *passwd_result = NULL;
158
159 if (getpwnam_r(name.c_str(), &passwd_storage, storage, sizeof(storage),
160 &passwd_result) != 0 || passwd_result == NULL) {
Ken Mixtera3249322011-03-03 08:47:38 -0800161 LOG(ERROR) << "Cannot find user named " << name;
Ken Mixter03403162010-08-18 15:23:16 -0700162 return false;
163 }
164
165 *uid = passwd_result->pw_uid;
166 *gid = passwd_result->pw_gid;
167 return true;
168}
169
170bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid,
Ken Mixter207694d2010-10-28 15:42:37 -0700171 FilePath *crash_directory,
172 bool *out_of_capacity) {
Ken Mixter03403162010-08-18 15:23:16 -0700173 uid_t default_user_id;
174 gid_t default_user_group;
175
Ken Mixter207694d2010-10-28 15:42:37 -0700176 if (out_of_capacity != NULL) *out_of_capacity = false;
177
Ken Mixter03403162010-08-18 15:23:16 -0700178 // For testing.
179 if (forced_crash_directory_ != NULL) {
180 *crash_directory = FilePath(forced_crash_directory_);
181 return true;
182 }
183
184 if (!GetUserInfoFromName(kDefaultUserName,
185 &default_user_id,
186 &default_user_group)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800187 LOG(ERROR) << "Could not find default user info";
Ken Mixter03403162010-08-18 15:23:16 -0700188 return false;
189 }
190 mode_t directory_mode;
191 uid_t directory_owner;
192 gid_t directory_group;
193 *crash_directory =
194 GetCrashDirectoryInfo(euid,
195 default_user_id,
196 default_user_group,
197 &directory_mode,
198 &directory_owner,
199 &directory_group);
200
201 if (!file_util::PathExists(*crash_directory)) {
202 // Create the spool directory with the appropriate mode (regardless of
203 // umask) and ownership.
204 mode_t old_mask = umask(0);
205 if (mkdir(crash_directory->value().c_str(), directory_mode) < 0 ||
206 chown(crash_directory->value().c_str(),
207 directory_owner,
208 directory_group) < 0) {
Ken Mixtera3249322011-03-03 08:47:38 -0800209 LOG(ERROR) << "Unable to create appropriate crash directory";
Ken Mixter03403162010-08-18 15:23:16 -0700210 return false;
211 }
212 umask(old_mask);
213 }
214
215 if (!file_util::PathExists(*crash_directory)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800216 LOG(ERROR) << "Unable to create crash directory "
217 << crash_directory->value().c_str();
Ken Mixter03403162010-08-18 15:23:16 -0700218 return false;
219 }
220
Ken Mixter04ec10f2010-08-26 16:02:02 -0700221 if (!CheckHasCapacity(*crash_directory)) {
Ken Mixter207694d2010-10-28 15:42:37 -0700222 if (out_of_capacity != NULL) *out_of_capacity = true;
Ken Mixter04ec10f2010-08-26 16:02:02 -0700223 return false;
224 }
225
Ken Mixter03403162010-08-18 15:23:16 -0700226 return true;
227}
Ken Mixter04ec10f2010-08-26 16:02:02 -0700228
Albert Chaulk426fcc02013-05-02 15:38:31 -0700229FilePath CrashCollector::GetProcessPath(pid_t pid) {
230 return FilePath(StringPrintf("/proc/%d", pid));
231}
232
233bool CrashCollector::GetSymlinkTarget(const FilePath &symlink,
234 FilePath *target) {
235 int max_size = 32;
236 scoped_array<char> buffer;
237 while (true) {
238 buffer.reset(new char[max_size + 1]);
239 ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size);
240 if (size < 0) {
241 int saved_errno = errno;
242 LOG(ERROR) << "Readlink failed on " << symlink.value() << " with "
243 << saved_errno;
244 return false;
245 }
246 buffer[size] = 0;
247 if (size == max_size) {
248 // Avoid overflow when doubling.
249 if (max_size * 2 > max_size) {
250 max_size *= 2;
251 continue;
252 } else {
253 return false;
254 }
255 }
256 break;
257 }
258
259 *target = FilePath(buffer.get());
260 return true;
261}
262
263bool CrashCollector::GetExecutableBaseNameFromPid(pid_t pid,
264 std::string *base_name) {
265 FilePath target;
266 FilePath process_path = GetProcessPath(pid);
267 FilePath exe_path = process_path.Append("exe");
268 if (!GetSymlinkTarget(exe_path, &target)) {
269 LOG(INFO) << "GetSymlinkTarget failed - Path " << process_path.value()
270 << " DirectoryExists: "
271 << file_util::DirectoryExists(process_path);
272 // Try to further diagnose exe readlink failure cause.
273 struct stat buf;
274 int stat_result = stat(exe_path.value().c_str(), &buf);
275 int saved_errno = errno;
276 if (stat_result < 0) {
277 LOG(INFO) << "stat " << exe_path.value() << " failed: " << stat_result
278 << " " << saved_errno;
279 } else {
280 LOG(INFO) << "stat " << exe_path.value() << " succeeded: st_mode="
281 << buf.st_mode;
282 }
283 return false;
284 }
285 *base_name = target.BaseName().value();
286 return true;
287}
288
Ken Mixter04ec10f2010-08-26 16:02:02 -0700289// Return true if the given crash directory has not already reached
290// maximum capacity.
291bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) {
292 DIR* dir = opendir(crash_directory.value().c_str());
293 if (!dir) {
294 return false;
295 }
296 struct dirent ent_buf;
297 struct dirent* ent;
Ken Mixter04ec10f2010-08-26 16:02:02 -0700298 bool full = false;
Ken Mixteree849c52010-09-30 15:30:10 -0700299 std::set<std::string> basenames;
Ken Mixter04ec10f2010-08-26 16:02:02 -0700300 while (readdir_r(dir, &ent_buf, &ent) == 0 && ent != NULL) {
301 if ((strcmp(ent->d_name, ".") == 0) ||
302 (strcmp(ent->d_name, "..") == 0))
303 continue;
304
Ken Mixteree849c52010-09-30 15:30:10 -0700305 std::string filename(ent->d_name);
306 size_t last_dot = filename.rfind(".");
307 std::string basename;
308 // If there is a valid looking extension, use the base part of the
309 // name. If the only dot is the first byte (aka a dot file), treat
310 // it as unique to avoid allowing a directory full of dot files
311 // from accumulating.
312 if (last_dot != std::string::npos && last_dot != 0)
313 basename = filename.substr(0, last_dot);
314 else
315 basename = filename;
316 basenames.insert(basename);
Ken Mixter04ec10f2010-08-26 16:02:02 -0700317
Ken Mixteree849c52010-09-30 15:30:10 -0700318 if (basenames.size() >= static_cast<size_t>(kMaxCrashDirectorySize)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800319 LOG(WARNING) << "Crash directory " << crash_directory.value()
320 << " already full with " << kMaxCrashDirectorySize
321 << " pending reports";
Ken Mixter04ec10f2010-08-26 16:02:02 -0700322 full = true;
323 break;
324 }
325 }
326 closedir(dir);
327 return !full;
328}
Ken Mixteree849c52010-09-30 15:30:10 -0700329
Ken Mixterc49dbd42010-12-14 17:44:11 -0800330bool CrashCollector::IsCommentLine(const std::string &line) {
331 size_t found = line.find_first_not_of(" ");
332 return found != std::string::npos && line[found] == '#';
333}
334
Ken Mixteree849c52010-09-30 15:30:10 -0700335bool CrashCollector::ReadKeyValueFile(
336 const FilePath &path,
337 const char separator,
338 std::map<std::string, std::string> *dictionary) {
339 std::string contents;
340 if (!file_util::ReadFileToString(path, &contents)) {
341 return false;
342 }
343 typedef std::vector<std::string> StringVector;
344 StringVector lines;
Chris Masone3ba6c5b2011-05-13 16:57:09 -0700345 base::SplitString(contents, '\n', &lines);
Ken Mixteree849c52010-09-30 15:30:10 -0700346 bool any_errors = false;
347 for (StringVector::iterator line = lines.begin(); line != lines.end();
348 ++line) {
349 // Allow empty strings.
350 if (line->empty())
351 continue;
Ken Mixterc49dbd42010-12-14 17:44:11 -0800352 // Allow comment lines.
353 if (IsCommentLine(*line))
354 continue;
Ken Mixteree849c52010-09-30 15:30:10 -0700355 StringVector sides;
Chris Masone3ba6c5b2011-05-13 16:57:09 -0700356 base::SplitString(*line, separator, &sides);
Ken Mixteree849c52010-09-30 15:30:10 -0700357 if (sides.size() != 2) {
358 any_errors = true;
359 continue;
360 }
361 dictionary->insert(std::pair<std::string, std::string>(sides[0], sides[1]));
362 }
363 return !any_errors;
364}
365
Ken Mixterc49dbd42010-12-14 17:44:11 -0800366bool CrashCollector::GetLogContents(const FilePath &config_path,
367 const std::string &exec_name,
368 const FilePath &output_file) {
369 std::map<std::string, std::string> log_commands;
370 if (!ReadKeyValueFile(config_path, ':', &log_commands)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800371 LOG(INFO) << "Unable to read log configuration file "
372 << config_path.value();
Ken Mixterc49dbd42010-12-14 17:44:11 -0800373 return false;
374 }
375
376 if (log_commands.find(exec_name) == log_commands.end())
377 return false;
378
Ken Mixtera3249322011-03-03 08:47:38 -0800379 chromeos::ProcessImpl diag_process;
380 diag_process.AddArg(kShellPath);
Ken Mixterc49dbd42010-12-14 17:44:11 -0800381 std::string shell_command = log_commands[exec_name];
Ken Mixtera3249322011-03-03 08:47:38 -0800382 diag_process.AddStringOption("-c", shell_command);
383 diag_process.RedirectOutput(output_file.value());
Ken Mixterc49dbd42010-12-14 17:44:11 -0800384
Ken Mixtera3249322011-03-03 08:47:38 -0800385 int result = diag_process.Run();
386 if (result != 0) {
387 LOG(INFO) << "Running shell command " << shell_command << "failed with: "
388 << result;
Ken Mixterc49dbd42010-12-14 17:44:11 -0800389 return false;
390 }
391 return true;
392}
393
Ken Mixterafcf8082010-10-26 14:45:01 -0700394void CrashCollector::AddCrashMetaData(const std::string &key,
395 const std::string &value) {
396 extra_metadata_.append(StringPrintf("%s=%s\n", key.c_str(), value.c_str()));
397}
398
Ken Mixteree849c52010-09-30 15:30:10 -0700399void CrashCollector::WriteCrashMetaData(const FilePath &meta_path,
Ken Mixterc909b692010-10-18 12:26:05 -0700400 const std::string &exec_name,
401 const std::string &payload_path) {
Ken Mixteree849c52010-09-30 15:30:10 -0700402 std::map<std::string, std::string> contents;
Ken Mixterafcf8082010-10-26 14:45:01 -0700403 if (!ReadKeyValueFile(FilePath(std::string(lsb_release_)), '=', &contents)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800404 LOG(ERROR) << "Problem parsing " << lsb_release_;
Ken Mixteree849c52010-09-30 15:30:10 -0700405 // Even though there was some failure, take as much as we could read.
406 }
407 std::string version("unknown");
408 std::map<std::string, std::string>::iterator i;
409 if ((i = contents.find("CHROMEOS_RELEASE_VERSION")) != contents.end()) {
410 version = i->second;
411 }
Ken Mixterc909b692010-10-18 12:26:05 -0700412 int64 payload_size = -1;
413 file_util::GetFileSize(FilePath(payload_path), &payload_size);
Ken Mixterafcf8082010-10-26 14:45:01 -0700414 std::string meta_data = StringPrintf("%sexec_name=%s\n"
Ken Mixteree849c52010-09-30 15:30:10 -0700415 "ver=%s\n"
Ken Mixter207694d2010-10-28 15:42:37 -0700416 "payload=%s\n"
Mike Frysinger65b4c1e2011-09-21 12:41:29 -0400417 "payload_size=%"PRId64"\n"
Ken Mixteree849c52010-09-30 15:30:10 -0700418 "done=1\n",
Ken Mixterafcf8082010-10-26 14:45:01 -0700419 extra_metadata_.c_str(),
Ken Mixteree849c52010-09-30 15:30:10 -0700420 exec_name.c_str(),
Ken Mixterc909b692010-10-18 12:26:05 -0700421 version.c_str(),
Ken Mixter207694d2010-10-28 15:42:37 -0700422 payload_path.c_str(),
Ken Mixterc909b692010-10-18 12:26:05 -0700423 payload_size);
Ken Mixter9b346472010-11-07 13:45:45 -0800424 // We must use WriteNewFile instead of file_util::WriteFile as we
425 // do not want to write with root access to a symlink that an attacker
426 // might have created.
427 if (WriteNewFile(meta_path, meta_data.c_str(), meta_data.size()) < 0) {
Ken Mixtera3249322011-03-03 08:47:38 -0800428 LOG(ERROR) << "Unable to write " << meta_path.value();
Ken Mixteree849c52010-09-30 15:30:10 -0700429 }
430}
Thieu Le1652fb22011-03-03 12:14:43 -0800431
432bool CrashCollector::IsCrashTestInProgress() {
433 return file_util::PathExists(FilePath(kCrashTestInProgressPath));
434}
Michael Krebs4fe30db2011-08-05 13:54:52 -0700435
436bool CrashCollector::IsDeveloperImage() {
437 // If we're testing crash reporter itself, we don't want to special-case
438 // for developer images.
439 if (IsCrashTestInProgress())
440 return false;
441 return file_util::PathExists(FilePath(kLeaveCoreFile));
442}
443
444bool CrashCollector::ShouldHandleChromeCrashes() {
445 // If we're testing crash reporter itself, we don't want to allow an
446 // override for chrome crashes. And, let's be conservative and only
447 // allow an override for developer images.
448 if (!IsCrashTestInProgress() && IsDeveloperImage()) {
449 // Check if there's an override to indicate we should indeed collect
450 // chrome crashes. This allows the crashes to still be tracked when
451 // they occur in autotests. See "crosbug.com/17987".
452 if (file_util::PathExists(FilePath(kCollectChromeFile)))
453 return true;
454 }
455 // We default to ignoring chrome crashes.
456 return false;
457}
458
459bool CrashCollector::IsUserSpecificDirectoryEnabled() {
460 return !ShouldHandleChromeCrashes();
461}