blob: d39d6f48a75dce3163120b8e06410ab9dba83397 [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
Mike Frysingerf19b5182013-05-17 19:36:47 -040019#include <dbus/dbus-glib-lowlevel.h>
20#include <glib.h>
21
Ken Mixter03403162010-08-18 15:23:16 -070022#include "base/file_util.h"
23#include "base/logging.h"
Mike Frysinger1a8780d2013-02-14 22:39:57 -050024#include "base/posix/eintr_wrapper.h"
Chris Masone8a68c7c2011-05-14 11:44:04 -070025#include "base/string_split.h"
Ken Mixter03403162010-08-18 15:23:16 -070026#include "base/string_util.h"
Mike Frysinger57b261c2012-04-11 14:47:09 -040027#include "base/stringprintf.h"
Mike Frysingerf19b5182013-05-17 19:36:47 -040028#include "chromeos/cryptohome.h"
29#include "chromeos/dbus/dbus.h"
30#include "chromeos/dbus/service_constants.h"
Ken Mixtera3249322011-03-03 08:47:38 -080031#include "chromeos/process.h"
Ken Mixter03403162010-08-18 15:23:16 -070032
Michael Krebs4fe30db2011-08-05 13:54:52 -070033static const char kCollectChromeFile[] =
34 "/mnt/stateful_partition/etc/collect_chrome_crashes";
35static const char kCrashTestInProgressPath[] = "/tmp/crash-test-in-progress";
Simon Quef70060c2012-04-09 19:07:07 -070036static const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf";
Ken Mixter03403162010-08-18 15:23:16 -070037static const char kDefaultUserName[] = "chronos";
Michael Krebs4fe30db2011-08-05 13:54:52 -070038static const char kLeaveCoreFile[] = "/root/.leave_core";
Ken Mixteree849c52010-09-30 15:30:10 -070039static const char kLsbRelease[] = "/etc/lsb-release";
Ken Mixterc49dbd42010-12-14 17:44:11 -080040static const char kShellPath[] = "/bin/sh";
Ken Mixter03403162010-08-18 15:23:16 -070041static const char kSystemCrashPath[] = "/var/spool/crash";
Albert Chaulk33dfd472013-06-19 15:34:13 -070042static const char kUploadVarPrefix[] = "upload_var_";
43static const char kUploadFilePrefix[] = "upload_file_";
Mike Frysingerf19b5182013-05-17 19:36:47 -040044// Normally this path is not used. Unfortunately, there are a few edge cases
45// where we need this. Any process that runs as kDefaultUserName that crashes
46// is consider a "user crash". That includes the initial Chrome browser that
47// runs the login screen. If that blows up, there is no logged in user yet,
48// so there is no per-user dir for us to stash things in. Instead we fallback
49// to this path as it is at least encrypted on a per-system basis.
50//
51// This also comes up when running autotests. The GUI is sitting at the login
52// screen while tests are sshing in, changing users, and triggering crashes as
53// the user (purposefully).
54static const char kFallbackUserCrashPath[] = "/home/chronos/crash";
Ken Mixter03403162010-08-18 15:23:16 -070055
56// Directory mode of the user crash spool directory.
57static const mode_t kUserCrashPathMode = 0755;
58
59// Directory mode of the system crash spool directory.
60static const mode_t kSystemCrashPathMode = 01755;
61
62static const uid_t kRootOwner = 0;
63static const uid_t kRootGroup = 0;
64
Ken Mixterda5db7a2010-09-17 13:50:42 -070065// Maximum crash reports per crash spool directory. Note that this is
66// a separate maximum from the maximum rate at which we upload these
67// diagnostics. The higher this rate is, the more space we allow for
68// core files, minidumps, and kcrash logs, and equivalently the more
69// processor and I/O bandwidth we dedicate to handling these crashes when
70// many occur at once. Also note that if core files are configured to
71// be left on the file system, we stop adding crashes when either the
72// number of core files or minidumps reaches this number.
73const int CrashCollector::kMaxCrashDirectorySize = 32;
Ken Mixter04ec10f2010-08-26 16:02:02 -070074
Simon Que9f90aca2013-02-19 17:19:52 -080075using base::FilePath;
76
Ken Mixterafcf8082010-10-26 14:45:01 -070077CrashCollector::CrashCollector()
78 : forced_crash_directory_(NULL),
Simon Queacc79382012-05-04 18:10:09 -070079 lsb_release_(kLsbRelease),
80 log_config_path_(kDefaultLogConfig) {
Ken Mixter03403162010-08-18 15:23:16 -070081}
82
83CrashCollector::~CrashCollector() {
84}
85
86void CrashCollector::Initialize(
87 CrashCollector::CountCrashFunction count_crash_function,
Ken Mixtera3249322011-03-03 08:47:38 -080088 CrashCollector::IsFeedbackAllowedFunction is_feedback_allowed_function) {
Ken Mixter03403162010-08-18 15:23:16 -070089 CHECK(count_crash_function != NULL);
90 CHECK(is_feedback_allowed_function != NULL);
Ken Mixter03403162010-08-18 15:23:16 -070091
92 count_crash_function_ = count_crash_function;
93 is_feedback_allowed_function_ = is_feedback_allowed_function;
Ken Mixter03403162010-08-18 15:23:16 -070094}
95
Ken Mixter9b346472010-11-07 13:45:45 -080096int CrashCollector::WriteNewFile(const FilePath &filename,
97 const char *data,
98 int size) {
99 int fd = HANDLE_EINTR(open(filename.value().c_str(),
100 O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0666));
101 if (fd < 0) {
102 return -1;
103 }
104
105 int rv = file_util::WriteFileDescriptor(fd, data, size);
106 HANDLE_EINTR(close(fd));
107 return rv;
108}
109
Ken Mixteree849c52010-09-30 15:30:10 -0700110std::string CrashCollector::Sanitize(const std::string &name) {
111 std::string result = name;
112 for (size_t i = 0; i < name.size(); ++i) {
113 if (!isalnum(result[i]) && result[i] != '_')
114 result[i] = '_';
115 }
116 return result;
117}
118
Ken Mixter03403162010-08-18 15:23:16 -0700119std::string CrashCollector::FormatDumpBasename(const std::string &exec_name,
120 time_t timestamp,
121 pid_t pid) {
122 struct tm tm;
123 localtime_r(&timestamp, &tm);
Ken Mixteree849c52010-09-30 15:30:10 -0700124 std::string sanitized_exec_name = Sanitize(exec_name);
Ken Mixter03403162010-08-18 15:23:16 -0700125 return StringPrintf("%s.%04d%02d%02d.%02d%02d%02d.%d",
Ken Mixteree849c52010-09-30 15:30:10 -0700126 sanitized_exec_name.c_str(),
Ken Mixter03403162010-08-18 15:23:16 -0700127 tm.tm_year + 1900,
128 tm.tm_mon + 1,
129 tm.tm_mday,
130 tm.tm_hour,
131 tm.tm_min,
132 tm.tm_sec,
133 pid);
134}
135
Ken Mixter207694d2010-10-28 15:42:37 -0700136FilePath CrashCollector::GetCrashPath(const FilePath &crash_directory,
137 const std::string &basename,
138 const std::string &extension) {
139 return crash_directory.Append(StringPrintf("%s.%s",
140 basename.c_str(),
141 extension.c_str()));
142}
143
Mike Frysingerf19b5182013-05-17 19:36:47 -0400144namespace {
145
146const char *GetGErrorMessage(const GError *error) {
147 if (!error)
148 return "Unknown error.";
149 return error->message;
150}
151
152}
153
154GHashTable *CrashCollector::GetActiveUserSessions(void) {
155 GHashTable *active_sessions = NULL;
156
157 chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection();
158 if (!dbus.HasConnection()) {
159 LOG(ERROR) << "Error connecting to system D-Bus";
160 return active_sessions;
161 }
162 chromeos::dbus::Proxy proxy(dbus,
163 login_manager::kSessionManagerServiceName,
164 login_manager::kSessionManagerServicePath,
165 login_manager::kSessionManagerInterface);
166 if (!proxy) {
167 LOG(ERROR) << "Error creating D-Bus proxy to interface "
168 << "'" << login_manager::kSessionManagerServiceName << "'";
169 return active_sessions;
170 }
171
172 // Request all the active sessions.
173 GError *gerror = NULL;
174 if (!dbus_g_proxy_call(proxy.gproxy(),
175 login_manager::kSessionManagerRetrieveActiveSessions,
176 &gerror, G_TYPE_INVALID,
177 DBUS_TYPE_G_STRING_STRING_HASHTABLE, &active_sessions,
178 G_TYPE_INVALID)) {
179 LOG(ERROR) << "Error performing D-Bus proxy call "
180 << "'"
181 << login_manager::kSessionManagerRetrieveActiveSessions << "'"
182 << ": " << GetGErrorMessage(gerror);
183 return active_sessions;
184 }
185
186 return active_sessions;
187}
188
189FilePath CrashCollector::GetUserCrashPath(void) {
190 // In this multiprofile world, there is no one-specific user dir anymore.
191 // Ask the session manager for the active ones, then just run with the
192 // first result we get back.
193 FilePath user_path = FilePath(kFallbackUserCrashPath);
194 GHashTable *active_sessions = GetActiveUserSessions();
195 if (!active_sessions)
196 return user_path;
197
198 GList *list = g_hash_table_get_values(active_sessions);
199 if (list) {
200 const char *salted_path = static_cast<const char *>(list->data);
Mike Frysinger37843a92013-06-11 17:03:59 -0400201 user_path = chromeos::cryptohome::home::GetHashedUserPath(salted_path)
202 .Append("crash");
Mike Frysingerf19b5182013-05-17 19:36:47 -0400203 g_list_free(list);
204 }
205
206 g_hash_table_destroy(active_sessions);
207
208 return user_path;
209}
210
Ken Mixter03403162010-08-18 15:23:16 -0700211FilePath CrashCollector::GetCrashDirectoryInfo(
212 uid_t process_euid,
213 uid_t default_user_id,
214 gid_t default_user_group,
215 mode_t *mode,
216 uid_t *directory_owner,
217 gid_t *directory_group) {
Michael Krebs4fe30db2011-08-05 13:54:52 -0700218 // TODO(mkrebs): This can go away once Chrome crashes are handled
219 // normally (see crosbug.com/5872).
220 // Check if the user crash directory should be used. If we are
221 // collecting chrome crashes during autotesting, we want to put them in
222 // the system crash directory so they are outside the cryptohome -- in
223 // case we are being run during logout (see crosbug.com/18637).
224 if (process_euid == default_user_id && IsUserSpecificDirectoryEnabled()) {
Ken Mixter03403162010-08-18 15:23:16 -0700225 *mode = kUserCrashPathMode;
226 *directory_owner = default_user_id;
227 *directory_group = default_user_group;
Mike Frysingerf19b5182013-05-17 19:36:47 -0400228 return GetUserCrashPath();
Ken Mixter03403162010-08-18 15:23:16 -0700229 } else {
230 *mode = kSystemCrashPathMode;
231 *directory_owner = kRootOwner;
232 *directory_group = kRootGroup;
233 return FilePath(kSystemCrashPath);
234 }
235}
236
237bool CrashCollector::GetUserInfoFromName(const std::string &name,
238 uid_t *uid,
239 gid_t *gid) {
240 char storage[256];
241 struct passwd passwd_storage;
242 struct passwd *passwd_result = NULL;
243
244 if (getpwnam_r(name.c_str(), &passwd_storage, storage, sizeof(storage),
245 &passwd_result) != 0 || passwd_result == NULL) {
Ken Mixtera3249322011-03-03 08:47:38 -0800246 LOG(ERROR) << "Cannot find user named " << name;
Ken Mixter03403162010-08-18 15:23:16 -0700247 return false;
248 }
249
250 *uid = passwd_result->pw_uid;
251 *gid = passwd_result->pw_gid;
252 return true;
253}
254
255bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid,
Ken Mixter207694d2010-10-28 15:42:37 -0700256 FilePath *crash_directory,
257 bool *out_of_capacity) {
Ken Mixter03403162010-08-18 15:23:16 -0700258 uid_t default_user_id;
259 gid_t default_user_group;
260
Ken Mixter207694d2010-10-28 15:42:37 -0700261 if (out_of_capacity != NULL) *out_of_capacity = false;
262
Ken Mixter03403162010-08-18 15:23:16 -0700263 // For testing.
264 if (forced_crash_directory_ != NULL) {
265 *crash_directory = FilePath(forced_crash_directory_);
266 return true;
267 }
268
269 if (!GetUserInfoFromName(kDefaultUserName,
270 &default_user_id,
271 &default_user_group)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800272 LOG(ERROR) << "Could not find default user info";
Ken Mixter03403162010-08-18 15:23:16 -0700273 return false;
274 }
275 mode_t directory_mode;
276 uid_t directory_owner;
277 gid_t directory_group;
278 *crash_directory =
279 GetCrashDirectoryInfo(euid,
280 default_user_id,
281 default_user_group,
282 &directory_mode,
283 &directory_owner,
284 &directory_group);
285
286 if (!file_util::PathExists(*crash_directory)) {
287 // Create the spool directory with the appropriate mode (regardless of
288 // umask) and ownership.
289 mode_t old_mask = umask(0);
290 if (mkdir(crash_directory->value().c_str(), directory_mode) < 0 ||
291 chown(crash_directory->value().c_str(),
292 directory_owner,
293 directory_group) < 0) {
Ken Mixtera3249322011-03-03 08:47:38 -0800294 LOG(ERROR) << "Unable to create appropriate crash directory";
Ken Mixter03403162010-08-18 15:23:16 -0700295 return false;
296 }
297 umask(old_mask);
298 }
299
300 if (!file_util::PathExists(*crash_directory)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800301 LOG(ERROR) << "Unable to create crash directory "
302 << crash_directory->value().c_str();
Ken Mixter03403162010-08-18 15:23:16 -0700303 return false;
304 }
305
Ken Mixter04ec10f2010-08-26 16:02:02 -0700306 if (!CheckHasCapacity(*crash_directory)) {
Ken Mixter207694d2010-10-28 15:42:37 -0700307 if (out_of_capacity != NULL) *out_of_capacity = true;
Ken Mixter04ec10f2010-08-26 16:02:02 -0700308 return false;
309 }
310
Ken Mixter03403162010-08-18 15:23:16 -0700311 return true;
312}
Ken Mixter04ec10f2010-08-26 16:02:02 -0700313
Albert Chaulk426fcc02013-05-02 15:38:31 -0700314FilePath CrashCollector::GetProcessPath(pid_t pid) {
315 return FilePath(StringPrintf("/proc/%d", pid));
316}
317
318bool CrashCollector::GetSymlinkTarget(const FilePath &symlink,
319 FilePath *target) {
320 int max_size = 32;
321 scoped_array<char> buffer;
322 while (true) {
323 buffer.reset(new char[max_size + 1]);
324 ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size);
325 if (size < 0) {
326 int saved_errno = errno;
327 LOG(ERROR) << "Readlink failed on " << symlink.value() << " with "
328 << saved_errno;
329 return false;
330 }
331 buffer[size] = 0;
332 if (size == max_size) {
333 // Avoid overflow when doubling.
334 if (max_size * 2 > max_size) {
335 max_size *= 2;
336 continue;
337 } else {
338 return false;
339 }
340 }
341 break;
342 }
343
344 *target = FilePath(buffer.get());
345 return true;
346}
347
348bool CrashCollector::GetExecutableBaseNameFromPid(pid_t pid,
349 std::string *base_name) {
350 FilePath target;
351 FilePath process_path = GetProcessPath(pid);
352 FilePath exe_path = process_path.Append("exe");
353 if (!GetSymlinkTarget(exe_path, &target)) {
354 LOG(INFO) << "GetSymlinkTarget failed - Path " << process_path.value()
355 << " DirectoryExists: "
356 << file_util::DirectoryExists(process_path);
357 // Try to further diagnose exe readlink failure cause.
358 struct stat buf;
359 int stat_result = stat(exe_path.value().c_str(), &buf);
360 int saved_errno = errno;
361 if (stat_result < 0) {
362 LOG(INFO) << "stat " << exe_path.value() << " failed: " << stat_result
363 << " " << saved_errno;
364 } else {
365 LOG(INFO) << "stat " << exe_path.value() << " succeeded: st_mode="
366 << buf.st_mode;
367 }
368 return false;
369 }
370 *base_name = target.BaseName().value();
371 return true;
372}
373
Ken Mixter04ec10f2010-08-26 16:02:02 -0700374// Return true if the given crash directory has not already reached
375// maximum capacity.
376bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) {
377 DIR* dir = opendir(crash_directory.value().c_str());
378 if (!dir) {
379 return false;
380 }
381 struct dirent ent_buf;
382 struct dirent* ent;
Ken Mixter04ec10f2010-08-26 16:02:02 -0700383 bool full = false;
Ken Mixteree849c52010-09-30 15:30:10 -0700384 std::set<std::string> basenames;
Ken Mixter04ec10f2010-08-26 16:02:02 -0700385 while (readdir_r(dir, &ent_buf, &ent) == 0 && ent != NULL) {
386 if ((strcmp(ent->d_name, ".") == 0) ||
387 (strcmp(ent->d_name, "..") == 0))
388 continue;
389
Ken Mixteree849c52010-09-30 15:30:10 -0700390 std::string filename(ent->d_name);
391 size_t last_dot = filename.rfind(".");
392 std::string basename;
393 // If there is a valid looking extension, use the base part of the
394 // name. If the only dot is the first byte (aka a dot file), treat
395 // it as unique to avoid allowing a directory full of dot files
396 // from accumulating.
397 if (last_dot != std::string::npos && last_dot != 0)
398 basename = filename.substr(0, last_dot);
399 else
400 basename = filename;
401 basenames.insert(basename);
Ken Mixter04ec10f2010-08-26 16:02:02 -0700402
Ken Mixteree849c52010-09-30 15:30:10 -0700403 if (basenames.size() >= static_cast<size_t>(kMaxCrashDirectorySize)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800404 LOG(WARNING) << "Crash directory " << crash_directory.value()
405 << " already full with " << kMaxCrashDirectorySize
406 << " pending reports";
Ken Mixter04ec10f2010-08-26 16:02:02 -0700407 full = true;
408 break;
409 }
410 }
411 closedir(dir);
412 return !full;
413}
Ken Mixteree849c52010-09-30 15:30:10 -0700414
Ken Mixterc49dbd42010-12-14 17:44:11 -0800415bool CrashCollector::IsCommentLine(const std::string &line) {
416 size_t found = line.find_first_not_of(" ");
417 return found != std::string::npos && line[found] == '#';
418}
419
Ken Mixteree849c52010-09-30 15:30:10 -0700420bool CrashCollector::ReadKeyValueFile(
421 const FilePath &path,
422 const char separator,
423 std::map<std::string, std::string> *dictionary) {
424 std::string contents;
425 if (!file_util::ReadFileToString(path, &contents)) {
426 return false;
427 }
428 typedef std::vector<std::string> StringVector;
429 StringVector lines;
Chris Masone3ba6c5b2011-05-13 16:57:09 -0700430 base::SplitString(contents, '\n', &lines);
Ken Mixteree849c52010-09-30 15:30:10 -0700431 bool any_errors = false;
432 for (StringVector::iterator line = lines.begin(); line != lines.end();
433 ++line) {
434 // Allow empty strings.
435 if (line->empty())
436 continue;
Ken Mixterc49dbd42010-12-14 17:44:11 -0800437 // Allow comment lines.
438 if (IsCommentLine(*line))
439 continue;
Ken Mixteree849c52010-09-30 15:30:10 -0700440 StringVector sides;
Chris Masone3ba6c5b2011-05-13 16:57:09 -0700441 base::SplitString(*line, separator, &sides);
Ken Mixteree849c52010-09-30 15:30:10 -0700442 if (sides.size() != 2) {
443 any_errors = true;
444 continue;
445 }
446 dictionary->insert(std::pair<std::string, std::string>(sides[0], sides[1]));
447 }
448 return !any_errors;
449}
450
Ken Mixterc49dbd42010-12-14 17:44:11 -0800451bool CrashCollector::GetLogContents(const FilePath &config_path,
452 const std::string &exec_name,
453 const FilePath &output_file) {
454 std::map<std::string, std::string> log_commands;
455 if (!ReadKeyValueFile(config_path, ':', &log_commands)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800456 LOG(INFO) << "Unable to read log configuration file "
457 << config_path.value();
Ken Mixterc49dbd42010-12-14 17:44:11 -0800458 return false;
459 }
460
461 if (log_commands.find(exec_name) == log_commands.end())
462 return false;
463
Ken Mixtera3249322011-03-03 08:47:38 -0800464 chromeos::ProcessImpl diag_process;
465 diag_process.AddArg(kShellPath);
Ken Mixterc49dbd42010-12-14 17:44:11 -0800466 std::string shell_command = log_commands[exec_name];
Ken Mixtera3249322011-03-03 08:47:38 -0800467 diag_process.AddStringOption("-c", shell_command);
468 diag_process.RedirectOutput(output_file.value());
Ken Mixterc49dbd42010-12-14 17:44:11 -0800469
Ken Mixtera3249322011-03-03 08:47:38 -0800470 int result = diag_process.Run();
471 if (result != 0) {
472 LOG(INFO) << "Running shell command " << shell_command << "failed with: "
473 << result;
Ken Mixterc49dbd42010-12-14 17:44:11 -0800474 return false;
475 }
476 return true;
477}
478
Ken Mixterafcf8082010-10-26 14:45:01 -0700479void CrashCollector::AddCrashMetaData(const std::string &key,
480 const std::string &value) {
481 extra_metadata_.append(StringPrintf("%s=%s\n", key.c_str(), value.c_str()));
482}
483
Albert Chaulk33dfd472013-06-19 15:34:13 -0700484void CrashCollector::AddCrashMetaUploadFile(const std::string &key,
485 const std::string &path) {
486 if (!path.empty())
487 AddCrashMetaData(kUploadFilePrefix + key, path);
488}
489
490void CrashCollector::AddCrashMetaUploadData(const std::string &key,
491 const std::string &value) {
492 if (!value.empty())
493 AddCrashMetaData(kUploadVarPrefix + key, value);
494}
495
Ken Mixteree849c52010-09-30 15:30:10 -0700496void CrashCollector::WriteCrashMetaData(const FilePath &meta_path,
Ken Mixterc909b692010-10-18 12:26:05 -0700497 const std::string &exec_name,
498 const std::string &payload_path) {
Ken Mixteree849c52010-09-30 15:30:10 -0700499 std::map<std::string, std::string> contents;
Ken Mixterafcf8082010-10-26 14:45:01 -0700500 if (!ReadKeyValueFile(FilePath(std::string(lsb_release_)), '=', &contents)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800501 LOG(ERROR) << "Problem parsing " << lsb_release_;
Ken Mixteree849c52010-09-30 15:30:10 -0700502 // Even though there was some failure, take as much as we could read.
503 }
504 std::string version("unknown");
505 std::map<std::string, std::string>::iterator i;
506 if ((i = contents.find("CHROMEOS_RELEASE_VERSION")) != contents.end()) {
507 version = i->second;
508 }
Ken Mixterc909b692010-10-18 12:26:05 -0700509 int64 payload_size = -1;
510 file_util::GetFileSize(FilePath(payload_path), &payload_size);
Ken Mixterafcf8082010-10-26 14:45:01 -0700511 std::string meta_data = StringPrintf("%sexec_name=%s\n"
Ken Mixteree849c52010-09-30 15:30:10 -0700512 "ver=%s\n"
Ken Mixter207694d2010-10-28 15:42:37 -0700513 "payload=%s\n"
Mike Frysinger65b4c1e2011-09-21 12:41:29 -0400514 "payload_size=%"PRId64"\n"
Ken Mixteree849c52010-09-30 15:30:10 -0700515 "done=1\n",
Ken Mixterafcf8082010-10-26 14:45:01 -0700516 extra_metadata_.c_str(),
Ken Mixteree849c52010-09-30 15:30:10 -0700517 exec_name.c_str(),
Ken Mixterc909b692010-10-18 12:26:05 -0700518 version.c_str(),
Ken Mixter207694d2010-10-28 15:42:37 -0700519 payload_path.c_str(),
Ken Mixterc909b692010-10-18 12:26:05 -0700520 payload_size);
Ken Mixter9b346472010-11-07 13:45:45 -0800521 // We must use WriteNewFile instead of file_util::WriteFile as we
522 // do not want to write with root access to a symlink that an attacker
523 // might have created.
524 if (WriteNewFile(meta_path, meta_data.c_str(), meta_data.size()) < 0) {
Ken Mixtera3249322011-03-03 08:47:38 -0800525 LOG(ERROR) << "Unable to write " << meta_path.value();
Ken Mixteree849c52010-09-30 15:30:10 -0700526 }
527}
Thieu Le1652fb22011-03-03 12:14:43 -0800528
529bool CrashCollector::IsCrashTestInProgress() {
530 return file_util::PathExists(FilePath(kCrashTestInProgressPath));
531}
Michael Krebs4fe30db2011-08-05 13:54:52 -0700532
533bool CrashCollector::IsDeveloperImage() {
534 // If we're testing crash reporter itself, we don't want to special-case
535 // for developer images.
536 if (IsCrashTestInProgress())
537 return false;
538 return file_util::PathExists(FilePath(kLeaveCoreFile));
539}
540
541bool CrashCollector::ShouldHandleChromeCrashes() {
542 // If we're testing crash reporter itself, we don't want to allow an
543 // override for chrome crashes. And, let's be conservative and only
544 // allow an override for developer images.
545 if (!IsCrashTestInProgress() && IsDeveloperImage()) {
546 // Check if there's an override to indicate we should indeed collect
547 // chrome crashes. This allows the crashes to still be tracked when
548 // they occur in autotests. See "crosbug.com/17987".
549 if (file_util::PathExists(FilePath(kCollectChromeFile)))
550 return true;
551 }
552 // We default to ignoring chrome crashes.
553 return false;
554}
555
556bool CrashCollector::IsUserSpecificDirectoryEnabled() {
557 return !ShouldHandleChromeCrashes();
558}