blob: a53bff9f479a669973ec125f99bbaa863b16e58c [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.
Alex Vakulenko7589aff2014-07-30 10:07:35 -07009#define __STDC_FORMAT_MACROS // PRId64
10#include <inttypes.h>
Ben Chanf3811f52014-08-26 06:46:38 -070011#include <linux/limits.h> // PATH_MAX
Ken Mixter03403162010-08-18 15:23:16 -070012#include <pwd.h> // For struct passwd.
13#include <sys/types.h> // for mode_t.
Ken Mixter9b346472010-11-07 13:45:45 -080014#include <sys/wait.h> // For waitpid.
15#include <unistd.h> // For execv and fork.
Ken Mixter03403162010-08-18 15:23:16 -070016
Ken Mixteree849c52010-09-30 15:30:10 -070017#include <set>
Ben Chan7e776902014-06-18 13:19:51 -070018#include <utility>
Simon Quef70060c2012-04-09 19:07:07 -070019#include <vector>
Ken Mixteree849c52010-09-30 15:30:10 -070020
Mike Frysingerf19b5182013-05-17 19:36:47 -040021#include <dbus/dbus-glib-lowlevel.h>
22#include <glib.h>
23
Ben Chanab6cc902014-09-05 08:21:06 -070024#include <base/files/file_util.h>
Ben Chan7e776902014-06-18 13:19:51 -070025#include <base/logging.h>
26#include <base/posix/eintr_wrapper.h>
27#include <base/strings/string_split.h>
28#include <base/strings/string_util.h>
29#include <base/strings/stringprintf.h>
30#include <chromeos/cryptohome.h>
31#include <chromeos/dbus/dbus.h>
32#include <chromeos/dbus/service_constants.h>
Daniel Erat731da332015-01-28 09:48:10 -070033#include <chromeos/key_value_store.h>
Ben Chan7e776902014-06-18 13:19:51 -070034#include <chromeos/process.h>
Ken Mixter03403162010-08-18 15:23:16 -070035
Daniel Erat731da332015-01-28 09:48:10 -070036namespace {
37
38const char kCollectChromeFile[] =
Michael Krebs4fe30db2011-08-05 13:54:52 -070039 "/mnt/stateful_partition/etc/collect_chrome_crashes";
Daniel Erat731da332015-01-28 09:48:10 -070040const char kCrashTestInProgressPath[] = "/tmp/crash-test-in-progress";
41const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf";
42const char kDefaultUserName[] = "chronos";
43const char kLeaveCoreFile[] = "/root/.leave_core";
44const char kLsbRelease[] = "/etc/lsb-release";
45const char kShellPath[] = "/bin/sh";
46const char kSystemCrashPath[] = "/var/spool/crash";
47const char kUploadVarPrefix[] = "upload_var_";
48const char kUploadFilePrefix[] = "upload_file_";
49
50// Key of the lsb-release entry containing the OS version.
51const char kLsbVersionKey[] = "CHROMEOS_RELEASE_VERSION";
52
Mike Frysingerf19b5182013-05-17 19:36:47 -040053// Normally this path is not used. Unfortunately, there are a few edge cases
54// where we need this. Any process that runs as kDefaultUserName that crashes
55// is consider a "user crash". That includes the initial Chrome browser that
56// runs the login screen. If that blows up, there is no logged in user yet,
57// so there is no per-user dir for us to stash things in. Instead we fallback
58// to this path as it is at least encrypted on a per-system basis.
59//
60// This also comes up when running autotests. The GUI is sitting at the login
61// screen while tests are sshing in, changing users, and triggering crashes as
62// the user (purposefully).
Daniel Erat731da332015-01-28 09:48:10 -070063const char kFallbackUserCrashPath[] = "/home/chronos/crash";
Ken Mixter03403162010-08-18 15:23:16 -070064
65// Directory mode of the user crash spool directory.
Daniel Erat731da332015-01-28 09:48:10 -070066const mode_t kUserCrashPathMode = 0755;
Ken Mixter03403162010-08-18 15:23:16 -070067
68// Directory mode of the system crash spool directory.
Daniel Erat731da332015-01-28 09:48:10 -070069const mode_t kSystemCrashPathMode = 01755;
Ken Mixter03403162010-08-18 15:23:16 -070070
Daniel Erat731da332015-01-28 09:48:10 -070071const uid_t kRootOwner = 0;
72const uid_t kRootGroup = 0;
73
74} // namespace
Ken Mixter03403162010-08-18 15:23:16 -070075
Ken Mixterda5db7a2010-09-17 13:50:42 -070076// Maximum crash reports per crash spool directory. Note that this is
77// a separate maximum from the maximum rate at which we upload these
78// diagnostics. The higher this rate is, the more space we allow for
79// core files, minidumps, and kcrash logs, and equivalently the more
80// processor and I/O bandwidth we dedicate to handling these crashes when
81// many occur at once. Also note that if core files are configured to
82// be left on the file system, we stop adding crashes when either the
83// number of core files or minidumps reaches this number.
84const int CrashCollector::kMaxCrashDirectorySize = 32;
Ken Mixter04ec10f2010-08-26 16:02:02 -070085
Simon Que9f90aca2013-02-19 17:19:52 -080086using base::FilePath;
Mike Frysingera557c112014-02-05 22:55:39 -050087using base::StringPrintf;
Simon Que9f90aca2013-02-19 17:19:52 -080088
Ken Mixterafcf8082010-10-26 14:45:01 -070089CrashCollector::CrashCollector()
Lei Zhang9b1f3002014-04-24 02:10:57 -070090 : lsb_release_(kLsbRelease),
Simon Queacc79382012-05-04 18:10:09 -070091 log_config_path_(kDefaultLogConfig) {
Ken Mixter03403162010-08-18 15:23:16 -070092}
93
94CrashCollector::~CrashCollector() {
95}
96
97void CrashCollector::Initialize(
98 CrashCollector::CountCrashFunction count_crash_function,
Ken Mixtera3249322011-03-03 08:47:38 -080099 CrashCollector::IsFeedbackAllowedFunction is_feedback_allowed_function) {
Ben Chan262d7982014-09-18 08:05:20 -0700100 CHECK(count_crash_function);
101 CHECK(is_feedback_allowed_function);
Ken Mixter03403162010-08-18 15:23:16 -0700102
103 count_crash_function_ = count_crash_function;
104 is_feedback_allowed_function_ = is_feedback_allowed_function;
Ken Mixter03403162010-08-18 15:23:16 -0700105}
106
Ken Mixter9b346472010-11-07 13:45:45 -0800107int CrashCollector::WriteNewFile(const FilePath &filename,
108 const char *data,
109 int size) {
110 int fd = HANDLE_EINTR(open(filename.value().c_str(),
111 O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0666));
112 if (fd < 0) {
113 return -1;
114 }
115
Alex Vakulenko859ee452014-12-10 12:52:31 -0800116 int rv = base::WriteFileDescriptor(fd, data, size) ? size : -1;
Mike Frysingerf1a50142014-05-14 16:05:09 -0400117 IGNORE_EINTR(close(fd));
Ken Mixter9b346472010-11-07 13:45:45 -0800118 return rv;
119}
120
Ken Mixteree849c52010-09-30 15:30:10 -0700121std::string CrashCollector::Sanitize(const std::string &name) {
Thiemo Nagel98950962014-05-13 19:48:32 +0200122 // Make sure the sanitized name does not include any periods.
123 // The logic in crash_sender relies on this.
Ken Mixteree849c52010-09-30 15:30:10 -0700124 std::string result = name;
125 for (size_t i = 0; i < name.size(); ++i) {
126 if (!isalnum(result[i]) && result[i] != '_')
127 result[i] = '_';
128 }
129 return result;
130}
131
Ken Mixter03403162010-08-18 15:23:16 -0700132std::string CrashCollector::FormatDumpBasename(const std::string &exec_name,
133 time_t timestamp,
134 pid_t pid) {
135 struct tm tm;
136 localtime_r(&timestamp, &tm);
Ken Mixteree849c52010-09-30 15:30:10 -0700137 std::string sanitized_exec_name = Sanitize(exec_name);
Ken Mixter03403162010-08-18 15:23:16 -0700138 return StringPrintf("%s.%04d%02d%02d.%02d%02d%02d.%d",
Ken Mixteree849c52010-09-30 15:30:10 -0700139 sanitized_exec_name.c_str(),
Ken Mixter03403162010-08-18 15:23:16 -0700140 tm.tm_year + 1900,
141 tm.tm_mon + 1,
142 tm.tm_mday,
143 tm.tm_hour,
144 tm.tm_min,
145 tm.tm_sec,
146 pid);
147}
148
Ken Mixter207694d2010-10-28 15:42:37 -0700149FilePath CrashCollector::GetCrashPath(const FilePath &crash_directory,
150 const std::string &basename,
151 const std::string &extension) {
152 return crash_directory.Append(StringPrintf("%s.%s",
153 basename.c_str(),
154 extension.c_str()));
155}
156
Mike Frysingerf19b5182013-05-17 19:36:47 -0400157namespace {
158
159const char *GetGErrorMessage(const GError *error) {
160 if (!error)
161 return "Unknown error.";
162 return error->message;
163}
164
165}
166
Ben Chanefec0b32014-08-12 08:52:11 -0700167GHashTable *CrashCollector::GetActiveUserSessions() {
Ben Chan262d7982014-09-18 08:05:20 -0700168 GHashTable *active_sessions = nullptr;
Mike Frysingerf19b5182013-05-17 19:36:47 -0400169
170 chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection();
171 if (!dbus.HasConnection()) {
172 LOG(ERROR) << "Error connecting to system D-Bus";
173 return active_sessions;
174 }
175 chromeos::dbus::Proxy proxy(dbus,
176 login_manager::kSessionManagerServiceName,
177 login_manager::kSessionManagerServicePath,
178 login_manager::kSessionManagerInterface);
179 if (!proxy) {
180 LOG(ERROR) << "Error creating D-Bus proxy to interface "
181 << "'" << login_manager::kSessionManagerServiceName << "'";
182 return active_sessions;
183 }
184
185 // Request all the active sessions.
Ben Chan262d7982014-09-18 08:05:20 -0700186 GError *gerror = nullptr;
Mike Frysingerf19b5182013-05-17 19:36:47 -0400187 if (!dbus_g_proxy_call(proxy.gproxy(),
188 login_manager::kSessionManagerRetrieveActiveSessions,
189 &gerror, G_TYPE_INVALID,
190 DBUS_TYPE_G_STRING_STRING_HASHTABLE, &active_sessions,
191 G_TYPE_INVALID)) {
192 LOG(ERROR) << "Error performing D-Bus proxy call "
193 << "'"
194 << login_manager::kSessionManagerRetrieveActiveSessions << "'"
195 << ": " << GetGErrorMessage(gerror);
196 return active_sessions;
197 }
198
199 return active_sessions;
200}
201
Ben Chanefec0b32014-08-12 08:52:11 -0700202FilePath CrashCollector::GetUserCrashPath() {
Mike Frysingerf19b5182013-05-17 19:36:47 -0400203 // In this multiprofile world, there is no one-specific user dir anymore.
204 // Ask the session manager for the active ones, then just run with the
205 // first result we get back.
206 FilePath user_path = FilePath(kFallbackUserCrashPath);
207 GHashTable *active_sessions = GetActiveUserSessions();
208 if (!active_sessions)
209 return user_path;
210
211 GList *list = g_hash_table_get_values(active_sessions);
212 if (list) {
213 const char *salted_path = static_cast<const char *>(list->data);
Mike Frysinger37843a92013-06-11 17:03:59 -0400214 user_path = chromeos::cryptohome::home::GetHashedUserPath(salted_path)
215 .Append("crash");
Mike Frysingerf19b5182013-05-17 19:36:47 -0400216 g_list_free(list);
217 }
218
219 g_hash_table_destroy(active_sessions);
220
221 return user_path;
222}
223
Ken Mixter03403162010-08-18 15:23:16 -0700224FilePath CrashCollector::GetCrashDirectoryInfo(
225 uid_t process_euid,
226 uid_t default_user_id,
227 gid_t default_user_group,
228 mode_t *mode,
229 uid_t *directory_owner,
230 gid_t *directory_group) {
Michael Krebs4fe30db2011-08-05 13:54:52 -0700231 // TODO(mkrebs): This can go away once Chrome crashes are handled
232 // normally (see crosbug.com/5872).
233 // Check if the user crash directory should be used. If we are
234 // collecting chrome crashes during autotesting, we want to put them in
235 // the system crash directory so they are outside the cryptohome -- in
236 // case we are being run during logout (see crosbug.com/18637).
237 if (process_euid == default_user_id && IsUserSpecificDirectoryEnabled()) {
Ken Mixter03403162010-08-18 15:23:16 -0700238 *mode = kUserCrashPathMode;
239 *directory_owner = default_user_id;
240 *directory_group = default_user_group;
Mike Frysingerf19b5182013-05-17 19:36:47 -0400241 return GetUserCrashPath();
Ken Mixter03403162010-08-18 15:23:16 -0700242 } else {
243 *mode = kSystemCrashPathMode;
244 *directory_owner = kRootOwner;
245 *directory_group = kRootGroup;
246 return FilePath(kSystemCrashPath);
247 }
248}
249
250bool CrashCollector::GetUserInfoFromName(const std::string &name,
251 uid_t *uid,
252 gid_t *gid) {
253 char storage[256];
254 struct passwd passwd_storage;
Ben Chan262d7982014-09-18 08:05:20 -0700255 struct passwd *passwd_result = nullptr;
Ken Mixter03403162010-08-18 15:23:16 -0700256
257 if (getpwnam_r(name.c_str(), &passwd_storage, storage, sizeof(storage),
Ben Chan262d7982014-09-18 08:05:20 -0700258 &passwd_result) != 0 || passwd_result == nullptr) {
Ken Mixtera3249322011-03-03 08:47:38 -0800259 LOG(ERROR) << "Cannot find user named " << name;
Ken Mixter03403162010-08-18 15:23:16 -0700260 return false;
261 }
262
263 *uid = passwd_result->pw_uid;
264 *gid = passwd_result->pw_gid;
265 return true;
266}
267
268bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid,
Ken Mixter207694d2010-10-28 15:42:37 -0700269 FilePath *crash_directory,
270 bool *out_of_capacity) {
Ken Mixter03403162010-08-18 15:23:16 -0700271 uid_t default_user_id;
272 gid_t default_user_group;
273
Ben Chan262d7982014-09-18 08:05:20 -0700274 if (out_of_capacity) *out_of_capacity = false;
Ken Mixter207694d2010-10-28 15:42:37 -0700275
Ken Mixter03403162010-08-18 15:23:16 -0700276 // For testing.
Lei Zhang9b1f3002014-04-24 02:10:57 -0700277 if (!forced_crash_directory_.empty()) {
278 *crash_directory = forced_crash_directory_;
Ken Mixter03403162010-08-18 15:23:16 -0700279 return true;
280 }
281
282 if (!GetUserInfoFromName(kDefaultUserName,
283 &default_user_id,
284 &default_user_group)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800285 LOG(ERROR) << "Could not find default user info";
Ken Mixter03403162010-08-18 15:23:16 -0700286 return false;
287 }
288 mode_t directory_mode;
289 uid_t directory_owner;
290 gid_t directory_group;
291 *crash_directory =
292 GetCrashDirectoryInfo(euid,
293 default_user_id,
294 default_user_group,
295 &directory_mode,
296 &directory_owner,
297 &directory_group);
298
Mike Frysingera557c112014-02-05 22:55:39 -0500299 if (!base::PathExists(*crash_directory)) {
Ken Mixter03403162010-08-18 15:23:16 -0700300 // Create the spool directory with the appropriate mode (regardless of
301 // umask) and ownership.
302 mode_t old_mask = umask(0);
303 if (mkdir(crash_directory->value().c_str(), directory_mode) < 0 ||
304 chown(crash_directory->value().c_str(),
305 directory_owner,
306 directory_group) < 0) {
Ken Mixtera3249322011-03-03 08:47:38 -0800307 LOG(ERROR) << "Unable to create appropriate crash directory";
Ken Mixter03403162010-08-18 15:23:16 -0700308 return false;
309 }
310 umask(old_mask);
311 }
312
Mike Frysingera557c112014-02-05 22:55:39 -0500313 if (!base::PathExists(*crash_directory)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800314 LOG(ERROR) << "Unable to create crash directory "
315 << crash_directory->value().c_str();
Ken Mixter03403162010-08-18 15:23:16 -0700316 return false;
317 }
318
Ken Mixter04ec10f2010-08-26 16:02:02 -0700319 if (!CheckHasCapacity(*crash_directory)) {
Ben Chan262d7982014-09-18 08:05:20 -0700320 if (out_of_capacity) *out_of_capacity = true;
Ken Mixter04ec10f2010-08-26 16:02:02 -0700321 return false;
322 }
323
Ken Mixter03403162010-08-18 15:23:16 -0700324 return true;
325}
Ken Mixter04ec10f2010-08-26 16:02:02 -0700326
Albert Chaulk426fcc02013-05-02 15:38:31 -0700327FilePath CrashCollector::GetProcessPath(pid_t pid) {
328 return FilePath(StringPrintf("/proc/%d", pid));
329}
330
331bool CrashCollector::GetSymlinkTarget(const FilePath &symlink,
Ben Chan8563d202014-01-27 19:30:13 -0800332 FilePath *target) {
Ben Chanf3811f52014-08-26 06:46:38 -0700333 ssize_t max_size = 64;
334 std::vector<char> buffer;
335
Albert Chaulk426fcc02013-05-02 15:38:31 -0700336 while (true) {
Ben Chanf3811f52014-08-26 06:46:38 -0700337 buffer.resize(max_size + 1);
338 ssize_t size = readlink(symlink.value().c_str(), buffer.data(), max_size);
Albert Chaulk426fcc02013-05-02 15:38:31 -0700339 if (size < 0) {
340 int saved_errno = errno;
341 LOG(ERROR) << "Readlink failed on " << symlink.value() << " with "
342 << saved_errno;
343 return false;
344 }
Ben Chanf3811f52014-08-26 06:46:38 -0700345
Albert Chaulk426fcc02013-05-02 15:38:31 -0700346 buffer[size] = 0;
347 if (size == max_size) {
Ben Chanf3811f52014-08-26 06:46:38 -0700348 max_size *= 2;
349 if (max_size > PATH_MAX) {
Albert Chaulk426fcc02013-05-02 15:38:31 -0700350 return false;
351 }
Ben Chanf3811f52014-08-26 06:46:38 -0700352 continue;
Albert Chaulk426fcc02013-05-02 15:38:31 -0700353 }
354 break;
355 }
356
Ben Chanf3811f52014-08-26 06:46:38 -0700357 *target = FilePath(buffer.data());
Albert Chaulk426fcc02013-05-02 15:38:31 -0700358 return true;
359}
360
361bool CrashCollector::GetExecutableBaseNameFromPid(pid_t pid,
362 std::string *base_name) {
363 FilePath target;
364 FilePath process_path = GetProcessPath(pid);
365 FilePath exe_path = process_path.Append("exe");
366 if (!GetSymlinkTarget(exe_path, &target)) {
367 LOG(INFO) << "GetSymlinkTarget failed - Path " << process_path.value()
368 << " DirectoryExists: "
Mike Frysingera557c112014-02-05 22:55:39 -0500369 << base::DirectoryExists(process_path);
Albert Chaulk426fcc02013-05-02 15:38:31 -0700370 // Try to further diagnose exe readlink failure cause.
371 struct stat buf;
372 int stat_result = stat(exe_path.value().c_str(), &buf);
373 int saved_errno = errno;
374 if (stat_result < 0) {
375 LOG(INFO) << "stat " << exe_path.value() << " failed: " << stat_result
376 << " " << saved_errno;
377 } else {
378 LOG(INFO) << "stat " << exe_path.value() << " succeeded: st_mode="
379 << buf.st_mode;
380 }
381 return false;
382 }
383 *base_name = target.BaseName().value();
384 return true;
385}
386
Ken Mixter04ec10f2010-08-26 16:02:02 -0700387// Return true if the given crash directory has not already reached
388// maximum capacity.
389bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) {
390 DIR* dir = opendir(crash_directory.value().c_str());
391 if (!dir) {
392 return false;
393 }
394 struct dirent ent_buf;
395 struct dirent* ent;
Ken Mixter04ec10f2010-08-26 16:02:02 -0700396 bool full = false;
Ken Mixteree849c52010-09-30 15:30:10 -0700397 std::set<std::string> basenames;
Ben Chan262d7982014-09-18 08:05:20 -0700398 while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) {
Ken Mixter04ec10f2010-08-26 16:02:02 -0700399 if ((strcmp(ent->d_name, ".") == 0) ||
400 (strcmp(ent->d_name, "..") == 0))
401 continue;
402
Ken Mixteree849c52010-09-30 15:30:10 -0700403 std::string filename(ent->d_name);
404 size_t last_dot = filename.rfind(".");
405 std::string basename;
406 // If there is a valid looking extension, use the base part of the
407 // name. If the only dot is the first byte (aka a dot file), treat
408 // it as unique to avoid allowing a directory full of dot files
409 // from accumulating.
410 if (last_dot != std::string::npos && last_dot != 0)
411 basename = filename.substr(0, last_dot);
412 else
413 basename = filename;
414 basenames.insert(basename);
Ken Mixter04ec10f2010-08-26 16:02:02 -0700415
Ken Mixteree849c52010-09-30 15:30:10 -0700416 if (basenames.size() >= static_cast<size_t>(kMaxCrashDirectorySize)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800417 LOG(WARNING) << "Crash directory " << crash_directory.value()
418 << " already full with " << kMaxCrashDirectorySize
419 << " pending reports";
Ken Mixter04ec10f2010-08-26 16:02:02 -0700420 full = true;
421 break;
422 }
423 }
424 closedir(dir);
425 return !full;
426}
Ken Mixteree849c52010-09-30 15:30:10 -0700427
Ken Mixterc49dbd42010-12-14 17:44:11 -0800428bool CrashCollector::GetLogContents(const FilePath &config_path,
429 const std::string &exec_name,
430 const FilePath &output_file) {
Daniel Erat731da332015-01-28 09:48:10 -0700431 chromeos::KeyValueStore store;
432 if (!store.Load(config_path)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800433 LOG(INFO) << "Unable to read log configuration file "
434 << config_path.value();
Ken Mixterc49dbd42010-12-14 17:44:11 -0800435 return false;
436 }
437
Daniel Erat731da332015-01-28 09:48:10 -0700438 std::string command;
439 if (!store.GetString(exec_name, &command))
Ken Mixterc49dbd42010-12-14 17:44:11 -0800440 return false;
441
Ken Mixtera3249322011-03-03 08:47:38 -0800442 chromeos::ProcessImpl diag_process;
443 diag_process.AddArg(kShellPath);
Daniel Erat731da332015-01-28 09:48:10 -0700444 diag_process.AddStringOption("-c", command);
Ken Mixtera3249322011-03-03 08:47:38 -0800445 diag_process.RedirectOutput(output_file.value());
Ken Mixterc49dbd42010-12-14 17:44:11 -0800446
Daniel Erat731da332015-01-28 09:48:10 -0700447 const int result = diag_process.Run();
Ken Mixtera3249322011-03-03 08:47:38 -0800448 if (result != 0) {
Daniel Erat731da332015-01-28 09:48:10 -0700449 LOG(INFO) << "Log command \"" << command << "\" exited with " << result;
Ken Mixterc49dbd42010-12-14 17:44:11 -0800450 return false;
451 }
452 return true;
453}
454
Ken Mixterafcf8082010-10-26 14:45:01 -0700455void CrashCollector::AddCrashMetaData(const std::string &key,
456 const std::string &value) {
457 extra_metadata_.append(StringPrintf("%s=%s\n", key.c_str(), value.c_str()));
458}
459
Albert Chaulk33dfd472013-06-19 15:34:13 -0700460void CrashCollector::AddCrashMetaUploadFile(const std::string &key,
461 const std::string &path) {
462 if (!path.empty())
463 AddCrashMetaData(kUploadFilePrefix + key, path);
464}
465
466void CrashCollector::AddCrashMetaUploadData(const std::string &key,
467 const std::string &value) {
468 if (!value.empty())
469 AddCrashMetaData(kUploadVarPrefix + key, value);
470}
471
Ken Mixteree849c52010-09-30 15:30:10 -0700472void CrashCollector::WriteCrashMetaData(const FilePath &meta_path,
Ken Mixterc909b692010-10-18 12:26:05 -0700473 const std::string &exec_name,
474 const std::string &payload_path) {
Daniel Erat731da332015-01-28 09:48:10 -0700475 chromeos::KeyValueStore store;
476 if (!store.Load(FilePath(lsb_release_))) {
Ken Mixtera3249322011-03-03 08:47:38 -0800477 LOG(ERROR) << "Problem parsing " << lsb_release_;
Ken Mixteree849c52010-09-30 15:30:10 -0700478 // Even though there was some failure, take as much as we could read.
479 }
Daniel Erat731da332015-01-28 09:48:10 -0700480
Ken Mixteree849c52010-09-30 15:30:10 -0700481 std::string version("unknown");
Daniel Erat731da332015-01-28 09:48:10 -0700482 if (!store.GetString(kLsbVersionKey, &version)) {
483 LOG(ERROR) << "Unable to read " << kLsbVersionKey << " from "
484 << lsb_release_;
Ken Mixteree849c52010-09-30 15:30:10 -0700485 }
Ben Chanf84ea212014-08-06 17:27:48 -0700486 int64_t payload_size = -1;
Mike Frysingera557c112014-02-05 22:55:39 -0500487 base::GetFileSize(FilePath(payload_path), &payload_size);
Ken Mixterafcf8082010-10-26 14:45:01 -0700488 std::string meta_data = StringPrintf("%sexec_name=%s\n"
Ken Mixteree849c52010-09-30 15:30:10 -0700489 "ver=%s\n"
Ken Mixter207694d2010-10-28 15:42:37 -0700490 "payload=%s\n"
Alex Vakulenkoeaf060c2014-08-08 09:36:48 -0700491 "payload_size=%" PRId64 "\n"
Ken Mixteree849c52010-09-30 15:30:10 -0700492 "done=1\n",
Ken Mixterafcf8082010-10-26 14:45:01 -0700493 extra_metadata_.c_str(),
Ken Mixteree849c52010-09-30 15:30:10 -0700494 exec_name.c_str(),
Ken Mixterc909b692010-10-18 12:26:05 -0700495 version.c_str(),
Ken Mixter207694d2010-10-28 15:42:37 -0700496 payload_path.c_str(),
Ken Mixterc909b692010-10-18 12:26:05 -0700497 payload_size);
Ben Chanf30c6412014-05-22 23:09:01 -0700498 // We must use WriteNewFile instead of base::WriteFile as we
Ken Mixter9b346472010-11-07 13:45:45 -0800499 // do not want to write with root access to a symlink that an attacker
500 // might have created.
501 if (WriteNewFile(meta_path, meta_data.c_str(), meta_data.size()) < 0) {
Ken Mixtera3249322011-03-03 08:47:38 -0800502 LOG(ERROR) << "Unable to write " << meta_path.value();
Ken Mixteree849c52010-09-30 15:30:10 -0700503 }
504}
Thieu Le1652fb22011-03-03 12:14:43 -0800505
506bool CrashCollector::IsCrashTestInProgress() {
Mike Frysingera557c112014-02-05 22:55:39 -0500507 return base::PathExists(FilePath(kCrashTestInProgressPath));
Thieu Le1652fb22011-03-03 12:14:43 -0800508}
Michael Krebs4fe30db2011-08-05 13:54:52 -0700509
510bool CrashCollector::IsDeveloperImage() {
511 // If we're testing crash reporter itself, we don't want to special-case
512 // for developer images.
513 if (IsCrashTestInProgress())
514 return false;
Mike Frysingera557c112014-02-05 22:55:39 -0500515 return base::PathExists(FilePath(kLeaveCoreFile));
Michael Krebs4fe30db2011-08-05 13:54:52 -0700516}
517
518bool CrashCollector::ShouldHandleChromeCrashes() {
519 // If we're testing crash reporter itself, we don't want to allow an
520 // override for chrome crashes. And, let's be conservative and only
521 // allow an override for developer images.
522 if (!IsCrashTestInProgress() && IsDeveloperImage()) {
523 // Check if there's an override to indicate we should indeed collect
524 // chrome crashes. This allows the crashes to still be tracked when
525 // they occur in autotests. See "crosbug.com/17987".
Mike Frysingera557c112014-02-05 22:55:39 -0500526 if (base::PathExists(FilePath(kCollectChromeFile)))
Michael Krebs4fe30db2011-08-05 13:54:52 -0700527 return true;
528 }
529 // We default to ignoring chrome crashes.
530 return false;
531}
532
533bool CrashCollector::IsUserSpecificDirectoryEnabled() {
534 return !ShouldHandleChromeCrashes();
535}