blob: 825ed08f2712fb188b8fcef35e268d8540715ba2 [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>
33#include <chromeos/process.h>
Ken Mixter03403162010-08-18 15:23:16 -070034
Michael Krebs4fe30db2011-08-05 13:54:52 -070035static const char kCollectChromeFile[] =
36 "/mnt/stateful_partition/etc/collect_chrome_crashes";
37static const char kCrashTestInProgressPath[] = "/tmp/crash-test-in-progress";
Simon Quef70060c2012-04-09 19:07:07 -070038static const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf";
Ken Mixter03403162010-08-18 15:23:16 -070039static const char kDefaultUserName[] = "chronos";
Michael Krebs4fe30db2011-08-05 13:54:52 -070040static const char kLeaveCoreFile[] = "/root/.leave_core";
Ken Mixteree849c52010-09-30 15:30:10 -070041static const char kLsbRelease[] = "/etc/lsb-release";
Ken Mixterc49dbd42010-12-14 17:44:11 -080042static const char kShellPath[] = "/bin/sh";
Ken Mixter03403162010-08-18 15:23:16 -070043static const char kSystemCrashPath[] = "/var/spool/crash";
Albert Chaulk33dfd472013-06-19 15:34:13 -070044static const char kUploadVarPrefix[] = "upload_var_";
45static const char kUploadFilePrefix[] = "upload_file_";
Mike Frysingerf19b5182013-05-17 19:36:47 -040046// Normally this path is not used. Unfortunately, there are a few edge cases
47// where we need this. Any process that runs as kDefaultUserName that crashes
48// is consider a "user crash". That includes the initial Chrome browser that
49// runs the login screen. If that blows up, there is no logged in user yet,
50// so there is no per-user dir for us to stash things in. Instead we fallback
51// to this path as it is at least encrypted on a per-system basis.
52//
53// This also comes up when running autotests. The GUI is sitting at the login
54// screen while tests are sshing in, changing users, and triggering crashes as
55// the user (purposefully).
56static const char kFallbackUserCrashPath[] = "/home/chronos/crash";
Ken Mixter03403162010-08-18 15:23:16 -070057
58// Directory mode of the user crash spool directory.
59static const mode_t kUserCrashPathMode = 0755;
60
61// Directory mode of the system crash spool directory.
62static const mode_t kSystemCrashPathMode = 01755;
63
64static const uid_t kRootOwner = 0;
65static const uid_t kRootGroup = 0;
66
Ken Mixterda5db7a2010-09-17 13:50:42 -070067// Maximum crash reports per crash spool directory. Note that this is
68// a separate maximum from the maximum rate at which we upload these
69// diagnostics. The higher this rate is, the more space we allow for
70// core files, minidumps, and kcrash logs, and equivalently the more
71// processor and I/O bandwidth we dedicate to handling these crashes when
72// many occur at once. Also note that if core files are configured to
73// be left on the file system, we stop adding crashes when either the
74// number of core files or minidumps reaches this number.
75const int CrashCollector::kMaxCrashDirectorySize = 32;
Ken Mixter04ec10f2010-08-26 16:02:02 -070076
Simon Que9f90aca2013-02-19 17:19:52 -080077using base::FilePath;
Mike Frysingera557c112014-02-05 22:55:39 -050078using base::StringPrintf;
Simon Que9f90aca2013-02-19 17:19:52 -080079
Ken Mixterafcf8082010-10-26 14:45:01 -070080CrashCollector::CrashCollector()
Lei Zhang9b1f3002014-04-24 02:10:57 -070081 : lsb_release_(kLsbRelease),
Simon Queacc79382012-05-04 18:10:09 -070082 log_config_path_(kDefaultLogConfig) {
Ken Mixter03403162010-08-18 15:23:16 -070083}
84
85CrashCollector::~CrashCollector() {
86}
87
88void CrashCollector::Initialize(
89 CrashCollector::CountCrashFunction count_crash_function,
Ken Mixtera3249322011-03-03 08:47:38 -080090 CrashCollector::IsFeedbackAllowedFunction is_feedback_allowed_function) {
Ben Chan262d7982014-09-18 08:05:20 -070091 CHECK(count_crash_function);
92 CHECK(is_feedback_allowed_function);
Ken Mixter03403162010-08-18 15:23:16 -070093
94 count_crash_function_ = count_crash_function;
95 is_feedback_allowed_function_ = is_feedback_allowed_function;
Ken Mixter03403162010-08-18 15:23:16 -070096}
97
Ken Mixter9b346472010-11-07 13:45:45 -080098int CrashCollector::WriteNewFile(const FilePath &filename,
99 const char *data,
100 int size) {
101 int fd = HANDLE_EINTR(open(filename.value().c_str(),
102 O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0666));
103 if (fd < 0) {
104 return -1;
105 }
106
Ben Chanf30c6412014-05-22 23:09:01 -0700107 int rv = base::WriteFileDescriptor(fd, data, size);
Mike Frysingerf1a50142014-05-14 16:05:09 -0400108 IGNORE_EINTR(close(fd));
Ken Mixter9b346472010-11-07 13:45:45 -0800109 return rv;
110}
111
Ken Mixteree849c52010-09-30 15:30:10 -0700112std::string CrashCollector::Sanitize(const std::string &name) {
Thiemo Nagel98950962014-05-13 19:48:32 +0200113 // Make sure the sanitized name does not include any periods.
114 // The logic in crash_sender relies on this.
Ken Mixteree849c52010-09-30 15:30:10 -0700115 std::string result = name;
116 for (size_t i = 0; i < name.size(); ++i) {
117 if (!isalnum(result[i]) && result[i] != '_')
118 result[i] = '_';
119 }
120 return result;
121}
122
Ken Mixter03403162010-08-18 15:23:16 -0700123std::string CrashCollector::FormatDumpBasename(const std::string &exec_name,
124 time_t timestamp,
125 pid_t pid) {
126 struct tm tm;
127 localtime_r(&timestamp, &tm);
Ken Mixteree849c52010-09-30 15:30:10 -0700128 std::string sanitized_exec_name = Sanitize(exec_name);
Ken Mixter03403162010-08-18 15:23:16 -0700129 return StringPrintf("%s.%04d%02d%02d.%02d%02d%02d.%d",
Ken Mixteree849c52010-09-30 15:30:10 -0700130 sanitized_exec_name.c_str(),
Ken Mixter03403162010-08-18 15:23:16 -0700131 tm.tm_year + 1900,
132 tm.tm_mon + 1,
133 tm.tm_mday,
134 tm.tm_hour,
135 tm.tm_min,
136 tm.tm_sec,
137 pid);
138}
139
Ken Mixter207694d2010-10-28 15:42:37 -0700140FilePath CrashCollector::GetCrashPath(const FilePath &crash_directory,
141 const std::string &basename,
142 const std::string &extension) {
143 return crash_directory.Append(StringPrintf("%s.%s",
144 basename.c_str(),
145 extension.c_str()));
146}
147
Mike Frysingerf19b5182013-05-17 19:36:47 -0400148namespace {
149
150const char *GetGErrorMessage(const GError *error) {
151 if (!error)
152 return "Unknown error.";
153 return error->message;
154}
155
156}
157
Ben Chanefec0b32014-08-12 08:52:11 -0700158GHashTable *CrashCollector::GetActiveUserSessions() {
Ben Chan262d7982014-09-18 08:05:20 -0700159 GHashTable *active_sessions = nullptr;
Mike Frysingerf19b5182013-05-17 19:36:47 -0400160
161 chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection();
162 if (!dbus.HasConnection()) {
163 LOG(ERROR) << "Error connecting to system D-Bus";
164 return active_sessions;
165 }
166 chromeos::dbus::Proxy proxy(dbus,
167 login_manager::kSessionManagerServiceName,
168 login_manager::kSessionManagerServicePath,
169 login_manager::kSessionManagerInterface);
170 if (!proxy) {
171 LOG(ERROR) << "Error creating D-Bus proxy to interface "
172 << "'" << login_manager::kSessionManagerServiceName << "'";
173 return active_sessions;
174 }
175
176 // Request all the active sessions.
Ben Chan262d7982014-09-18 08:05:20 -0700177 GError *gerror = nullptr;
Mike Frysingerf19b5182013-05-17 19:36:47 -0400178 if (!dbus_g_proxy_call(proxy.gproxy(),
179 login_manager::kSessionManagerRetrieveActiveSessions,
180 &gerror, G_TYPE_INVALID,
181 DBUS_TYPE_G_STRING_STRING_HASHTABLE, &active_sessions,
182 G_TYPE_INVALID)) {
183 LOG(ERROR) << "Error performing D-Bus proxy call "
184 << "'"
185 << login_manager::kSessionManagerRetrieveActiveSessions << "'"
186 << ": " << GetGErrorMessage(gerror);
187 return active_sessions;
188 }
189
190 return active_sessions;
191}
192
Ben Chanefec0b32014-08-12 08:52:11 -0700193FilePath CrashCollector::GetUserCrashPath() {
Mike Frysingerf19b5182013-05-17 19:36:47 -0400194 // In this multiprofile world, there is no one-specific user dir anymore.
195 // Ask the session manager for the active ones, then just run with the
196 // first result we get back.
197 FilePath user_path = FilePath(kFallbackUserCrashPath);
198 GHashTable *active_sessions = GetActiveUserSessions();
199 if (!active_sessions)
200 return user_path;
201
202 GList *list = g_hash_table_get_values(active_sessions);
203 if (list) {
204 const char *salted_path = static_cast<const char *>(list->data);
Mike Frysinger37843a92013-06-11 17:03:59 -0400205 user_path = chromeos::cryptohome::home::GetHashedUserPath(salted_path)
206 .Append("crash");
Mike Frysingerf19b5182013-05-17 19:36:47 -0400207 g_list_free(list);
208 }
209
210 g_hash_table_destroy(active_sessions);
211
212 return user_path;
213}
214
Ken Mixter03403162010-08-18 15:23:16 -0700215FilePath CrashCollector::GetCrashDirectoryInfo(
216 uid_t process_euid,
217 uid_t default_user_id,
218 gid_t default_user_group,
219 mode_t *mode,
220 uid_t *directory_owner,
221 gid_t *directory_group) {
Michael Krebs4fe30db2011-08-05 13:54:52 -0700222 // TODO(mkrebs): This can go away once Chrome crashes are handled
223 // normally (see crosbug.com/5872).
224 // Check if the user crash directory should be used. If we are
225 // collecting chrome crashes during autotesting, we want to put them in
226 // the system crash directory so they are outside the cryptohome -- in
227 // case we are being run during logout (see crosbug.com/18637).
228 if (process_euid == default_user_id && IsUserSpecificDirectoryEnabled()) {
Ken Mixter03403162010-08-18 15:23:16 -0700229 *mode = kUserCrashPathMode;
230 *directory_owner = default_user_id;
231 *directory_group = default_user_group;
Mike Frysingerf19b5182013-05-17 19:36:47 -0400232 return GetUserCrashPath();
Ken Mixter03403162010-08-18 15:23:16 -0700233 } else {
234 *mode = kSystemCrashPathMode;
235 *directory_owner = kRootOwner;
236 *directory_group = kRootGroup;
237 return FilePath(kSystemCrashPath);
238 }
239}
240
241bool CrashCollector::GetUserInfoFromName(const std::string &name,
242 uid_t *uid,
243 gid_t *gid) {
244 char storage[256];
245 struct passwd passwd_storage;
Ben Chan262d7982014-09-18 08:05:20 -0700246 struct passwd *passwd_result = nullptr;
Ken Mixter03403162010-08-18 15:23:16 -0700247
248 if (getpwnam_r(name.c_str(), &passwd_storage, storage, sizeof(storage),
Ben Chan262d7982014-09-18 08:05:20 -0700249 &passwd_result) != 0 || passwd_result == nullptr) {
Ken Mixtera3249322011-03-03 08:47:38 -0800250 LOG(ERROR) << "Cannot find user named " << name;
Ken Mixter03403162010-08-18 15:23:16 -0700251 return false;
252 }
253
254 *uid = passwd_result->pw_uid;
255 *gid = passwd_result->pw_gid;
256 return true;
257}
258
259bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid,
Ken Mixter207694d2010-10-28 15:42:37 -0700260 FilePath *crash_directory,
261 bool *out_of_capacity) {
Ken Mixter03403162010-08-18 15:23:16 -0700262 uid_t default_user_id;
263 gid_t default_user_group;
264
Ben Chan262d7982014-09-18 08:05:20 -0700265 if (out_of_capacity) *out_of_capacity = false;
Ken Mixter207694d2010-10-28 15:42:37 -0700266
Ken Mixter03403162010-08-18 15:23:16 -0700267 // For testing.
Lei Zhang9b1f3002014-04-24 02:10:57 -0700268 if (!forced_crash_directory_.empty()) {
269 *crash_directory = forced_crash_directory_;
Ken Mixter03403162010-08-18 15:23:16 -0700270 return true;
271 }
272
273 if (!GetUserInfoFromName(kDefaultUserName,
274 &default_user_id,
275 &default_user_group)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800276 LOG(ERROR) << "Could not find default user info";
Ken Mixter03403162010-08-18 15:23:16 -0700277 return false;
278 }
279 mode_t directory_mode;
280 uid_t directory_owner;
281 gid_t directory_group;
282 *crash_directory =
283 GetCrashDirectoryInfo(euid,
284 default_user_id,
285 default_user_group,
286 &directory_mode,
287 &directory_owner,
288 &directory_group);
289
Mike Frysingera557c112014-02-05 22:55:39 -0500290 if (!base::PathExists(*crash_directory)) {
Ken Mixter03403162010-08-18 15:23:16 -0700291 // Create the spool directory with the appropriate mode (regardless of
292 // umask) and ownership.
293 mode_t old_mask = umask(0);
294 if (mkdir(crash_directory->value().c_str(), directory_mode) < 0 ||
295 chown(crash_directory->value().c_str(),
296 directory_owner,
297 directory_group) < 0) {
Ken Mixtera3249322011-03-03 08:47:38 -0800298 LOG(ERROR) << "Unable to create appropriate crash directory";
Ken Mixter03403162010-08-18 15:23:16 -0700299 return false;
300 }
301 umask(old_mask);
302 }
303
Mike Frysingera557c112014-02-05 22:55:39 -0500304 if (!base::PathExists(*crash_directory)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800305 LOG(ERROR) << "Unable to create crash directory "
306 << crash_directory->value().c_str();
Ken Mixter03403162010-08-18 15:23:16 -0700307 return false;
308 }
309
Ken Mixter04ec10f2010-08-26 16:02:02 -0700310 if (!CheckHasCapacity(*crash_directory)) {
Ben Chan262d7982014-09-18 08:05:20 -0700311 if (out_of_capacity) *out_of_capacity = true;
Ken Mixter04ec10f2010-08-26 16:02:02 -0700312 return false;
313 }
314
Ken Mixter03403162010-08-18 15:23:16 -0700315 return true;
316}
Ken Mixter04ec10f2010-08-26 16:02:02 -0700317
Albert Chaulk426fcc02013-05-02 15:38:31 -0700318FilePath CrashCollector::GetProcessPath(pid_t pid) {
319 return FilePath(StringPrintf("/proc/%d", pid));
320}
321
322bool CrashCollector::GetSymlinkTarget(const FilePath &symlink,
Ben Chan8563d202014-01-27 19:30:13 -0800323 FilePath *target) {
Ben Chanf3811f52014-08-26 06:46:38 -0700324 ssize_t max_size = 64;
325 std::vector<char> buffer;
326
Albert Chaulk426fcc02013-05-02 15:38:31 -0700327 while (true) {
Ben Chanf3811f52014-08-26 06:46:38 -0700328 buffer.resize(max_size + 1);
329 ssize_t size = readlink(symlink.value().c_str(), buffer.data(), max_size);
Albert Chaulk426fcc02013-05-02 15:38:31 -0700330 if (size < 0) {
331 int saved_errno = errno;
332 LOG(ERROR) << "Readlink failed on " << symlink.value() << " with "
333 << saved_errno;
334 return false;
335 }
Ben Chanf3811f52014-08-26 06:46:38 -0700336
Albert Chaulk426fcc02013-05-02 15:38:31 -0700337 buffer[size] = 0;
338 if (size == max_size) {
Ben Chanf3811f52014-08-26 06:46:38 -0700339 max_size *= 2;
340 if (max_size > PATH_MAX) {
Albert Chaulk426fcc02013-05-02 15:38:31 -0700341 return false;
342 }
Ben Chanf3811f52014-08-26 06:46:38 -0700343 continue;
Albert Chaulk426fcc02013-05-02 15:38:31 -0700344 }
345 break;
346 }
347
Ben Chanf3811f52014-08-26 06:46:38 -0700348 *target = FilePath(buffer.data());
Albert Chaulk426fcc02013-05-02 15:38:31 -0700349 return true;
350}
351
352bool CrashCollector::GetExecutableBaseNameFromPid(pid_t pid,
353 std::string *base_name) {
354 FilePath target;
355 FilePath process_path = GetProcessPath(pid);
356 FilePath exe_path = process_path.Append("exe");
357 if (!GetSymlinkTarget(exe_path, &target)) {
358 LOG(INFO) << "GetSymlinkTarget failed - Path " << process_path.value()
359 << " DirectoryExists: "
Mike Frysingera557c112014-02-05 22:55:39 -0500360 << base::DirectoryExists(process_path);
Albert Chaulk426fcc02013-05-02 15:38:31 -0700361 // Try to further diagnose exe readlink failure cause.
362 struct stat buf;
363 int stat_result = stat(exe_path.value().c_str(), &buf);
364 int saved_errno = errno;
365 if (stat_result < 0) {
366 LOG(INFO) << "stat " << exe_path.value() << " failed: " << stat_result
367 << " " << saved_errno;
368 } else {
369 LOG(INFO) << "stat " << exe_path.value() << " succeeded: st_mode="
370 << buf.st_mode;
371 }
372 return false;
373 }
374 *base_name = target.BaseName().value();
375 return true;
376}
377
Ken Mixter04ec10f2010-08-26 16:02:02 -0700378// Return true if the given crash directory has not already reached
379// maximum capacity.
380bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) {
381 DIR* dir = opendir(crash_directory.value().c_str());
382 if (!dir) {
383 return false;
384 }
385 struct dirent ent_buf;
386 struct dirent* ent;
Ken Mixter04ec10f2010-08-26 16:02:02 -0700387 bool full = false;
Ken Mixteree849c52010-09-30 15:30:10 -0700388 std::set<std::string> basenames;
Ben Chan262d7982014-09-18 08:05:20 -0700389 while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) {
Ken Mixter04ec10f2010-08-26 16:02:02 -0700390 if ((strcmp(ent->d_name, ".") == 0) ||
391 (strcmp(ent->d_name, "..") == 0))
392 continue;
393
Ken Mixteree849c52010-09-30 15:30:10 -0700394 std::string filename(ent->d_name);
395 size_t last_dot = filename.rfind(".");
396 std::string basename;
397 // If there is a valid looking extension, use the base part of the
398 // name. If the only dot is the first byte (aka a dot file), treat
399 // it as unique to avoid allowing a directory full of dot files
400 // from accumulating.
401 if (last_dot != std::string::npos && last_dot != 0)
402 basename = filename.substr(0, last_dot);
403 else
404 basename = filename;
405 basenames.insert(basename);
Ken Mixter04ec10f2010-08-26 16:02:02 -0700406
Ken Mixteree849c52010-09-30 15:30:10 -0700407 if (basenames.size() >= static_cast<size_t>(kMaxCrashDirectorySize)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800408 LOG(WARNING) << "Crash directory " << crash_directory.value()
409 << " already full with " << kMaxCrashDirectorySize
410 << " pending reports";
Ken Mixter04ec10f2010-08-26 16:02:02 -0700411 full = true;
412 break;
413 }
414 }
415 closedir(dir);
416 return !full;
417}
Ken Mixteree849c52010-09-30 15:30:10 -0700418
Ken Mixterc49dbd42010-12-14 17:44:11 -0800419bool CrashCollector::IsCommentLine(const std::string &line) {
420 size_t found = line.find_first_not_of(" ");
421 return found != std::string::npos && line[found] == '#';
422}
423
Ken Mixteree849c52010-09-30 15:30:10 -0700424bool CrashCollector::ReadKeyValueFile(
425 const FilePath &path,
426 const char separator,
427 std::map<std::string, std::string> *dictionary) {
428 std::string contents;
Mike Frysingera557c112014-02-05 22:55:39 -0500429 if (!base::ReadFileToString(path, &contents)) {
Ken Mixteree849c52010-09-30 15:30:10 -0700430 return false;
431 }
432 typedef std::vector<std::string> StringVector;
433 StringVector lines;
Chris Masone3ba6c5b2011-05-13 16:57:09 -0700434 base::SplitString(contents, '\n', &lines);
Ken Mixteree849c52010-09-30 15:30:10 -0700435 bool any_errors = false;
436 for (StringVector::iterator line = lines.begin(); line != lines.end();
437 ++line) {
438 // Allow empty strings.
439 if (line->empty())
440 continue;
Ken Mixterc49dbd42010-12-14 17:44:11 -0800441 // Allow comment lines.
442 if (IsCommentLine(*line))
443 continue;
Ken Mixteree849c52010-09-30 15:30:10 -0700444 StringVector sides;
Chris Masone3ba6c5b2011-05-13 16:57:09 -0700445 base::SplitString(*line, separator, &sides);
Ken Mixteree849c52010-09-30 15:30:10 -0700446 if (sides.size() != 2) {
447 any_errors = true;
448 continue;
449 }
450 dictionary->insert(std::pair<std::string, std::string>(sides[0], sides[1]));
451 }
452 return !any_errors;
453}
454
Ken Mixterc49dbd42010-12-14 17:44:11 -0800455bool CrashCollector::GetLogContents(const FilePath &config_path,
456 const std::string &exec_name,
457 const FilePath &output_file) {
458 std::map<std::string, std::string> log_commands;
459 if (!ReadKeyValueFile(config_path, ':', &log_commands)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800460 LOG(INFO) << "Unable to read log configuration file "
461 << config_path.value();
Ken Mixterc49dbd42010-12-14 17:44:11 -0800462 return false;
463 }
464
465 if (log_commands.find(exec_name) == log_commands.end())
466 return false;
467
Ken Mixtera3249322011-03-03 08:47:38 -0800468 chromeos::ProcessImpl diag_process;
469 diag_process.AddArg(kShellPath);
Ken Mixterc49dbd42010-12-14 17:44:11 -0800470 std::string shell_command = log_commands[exec_name];
Ken Mixtera3249322011-03-03 08:47:38 -0800471 diag_process.AddStringOption("-c", shell_command);
472 diag_process.RedirectOutput(output_file.value());
Ken Mixterc49dbd42010-12-14 17:44:11 -0800473
Ken Mixtera3249322011-03-03 08:47:38 -0800474 int result = diag_process.Run();
475 if (result != 0) {
476 LOG(INFO) << "Running shell command " << shell_command << "failed with: "
477 << result;
Ken Mixterc49dbd42010-12-14 17:44:11 -0800478 return false;
479 }
480 return true;
481}
482
Ken Mixterafcf8082010-10-26 14:45:01 -0700483void CrashCollector::AddCrashMetaData(const std::string &key,
484 const std::string &value) {
485 extra_metadata_.append(StringPrintf("%s=%s\n", key.c_str(), value.c_str()));
486}
487
Albert Chaulk33dfd472013-06-19 15:34:13 -0700488void CrashCollector::AddCrashMetaUploadFile(const std::string &key,
489 const std::string &path) {
490 if (!path.empty())
491 AddCrashMetaData(kUploadFilePrefix + key, path);
492}
493
494void CrashCollector::AddCrashMetaUploadData(const std::string &key,
495 const std::string &value) {
496 if (!value.empty())
497 AddCrashMetaData(kUploadVarPrefix + key, value);
498}
499
Ken Mixteree849c52010-09-30 15:30:10 -0700500void CrashCollector::WriteCrashMetaData(const FilePath &meta_path,
Ken Mixterc909b692010-10-18 12:26:05 -0700501 const std::string &exec_name,
502 const std::string &payload_path) {
Ken Mixteree849c52010-09-30 15:30:10 -0700503 std::map<std::string, std::string> contents;
Lei Zhang9b1f3002014-04-24 02:10:57 -0700504 if (!ReadKeyValueFile(FilePath(lsb_release_), '=', &contents)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800505 LOG(ERROR) << "Problem parsing " << lsb_release_;
Ken Mixteree849c52010-09-30 15:30:10 -0700506 // Even though there was some failure, take as much as we could read.
507 }
508 std::string version("unknown");
509 std::map<std::string, std::string>::iterator i;
510 if ((i = contents.find("CHROMEOS_RELEASE_VERSION")) != contents.end()) {
511 version = i->second;
512 }
Ben Chanf84ea212014-08-06 17:27:48 -0700513 int64_t payload_size = -1;
Mike Frysingera557c112014-02-05 22:55:39 -0500514 base::GetFileSize(FilePath(payload_path), &payload_size);
Ken Mixterafcf8082010-10-26 14:45:01 -0700515 std::string meta_data = StringPrintf("%sexec_name=%s\n"
Ken Mixteree849c52010-09-30 15:30:10 -0700516 "ver=%s\n"
Ken Mixter207694d2010-10-28 15:42:37 -0700517 "payload=%s\n"
Alex Vakulenkoeaf060c2014-08-08 09:36:48 -0700518 "payload_size=%" PRId64 "\n"
Ken Mixteree849c52010-09-30 15:30:10 -0700519 "done=1\n",
Ken Mixterafcf8082010-10-26 14:45:01 -0700520 extra_metadata_.c_str(),
Ken Mixteree849c52010-09-30 15:30:10 -0700521 exec_name.c_str(),
Ken Mixterc909b692010-10-18 12:26:05 -0700522 version.c_str(),
Ken Mixter207694d2010-10-28 15:42:37 -0700523 payload_path.c_str(),
Ken Mixterc909b692010-10-18 12:26:05 -0700524 payload_size);
Ben Chanf30c6412014-05-22 23:09:01 -0700525 // We must use WriteNewFile instead of base::WriteFile as we
Ken Mixter9b346472010-11-07 13:45:45 -0800526 // do not want to write with root access to a symlink that an attacker
527 // might have created.
528 if (WriteNewFile(meta_path, meta_data.c_str(), meta_data.size()) < 0) {
Ken Mixtera3249322011-03-03 08:47:38 -0800529 LOG(ERROR) << "Unable to write " << meta_path.value();
Ken Mixteree849c52010-09-30 15:30:10 -0700530 }
531}
Thieu Le1652fb22011-03-03 12:14:43 -0800532
533bool CrashCollector::IsCrashTestInProgress() {
Mike Frysingera557c112014-02-05 22:55:39 -0500534 return base::PathExists(FilePath(kCrashTestInProgressPath));
Thieu Le1652fb22011-03-03 12:14:43 -0800535}
Michael Krebs4fe30db2011-08-05 13:54:52 -0700536
537bool CrashCollector::IsDeveloperImage() {
538 // If we're testing crash reporter itself, we don't want to special-case
539 // for developer images.
540 if (IsCrashTestInProgress())
541 return false;
Mike Frysingera557c112014-02-05 22:55:39 -0500542 return base::PathExists(FilePath(kLeaveCoreFile));
Michael Krebs4fe30db2011-08-05 13:54:52 -0700543}
544
545bool CrashCollector::ShouldHandleChromeCrashes() {
546 // If we're testing crash reporter itself, we don't want to allow an
547 // override for chrome crashes. And, let's be conservative and only
548 // allow an override for developer images.
549 if (!IsCrashTestInProgress() && IsDeveloperImage()) {
550 // Check if there's an override to indicate we should indeed collect
551 // chrome crashes. This allows the crashes to still be tracked when
552 // they occur in autotests. See "crosbug.com/17987".
Mike Frysingera557c112014-02-05 22:55:39 -0500553 if (base::PathExists(FilePath(kCollectChromeFile)))
Michael Krebs4fe30db2011-08-05 13:54:52 -0700554 return true;
555 }
556 // We default to ignoring chrome crashes.
557 return false;
558}
559
560bool CrashCollector::IsUserSpecificDirectoryEnabled() {
561 return !ShouldHandleChromeCrashes();
562}