blob: e051a4ac342f301ca37d5a138a5285a81332ac51 [file] [log] [blame]
Chris Sosae4a86032010-06-16 17:08:34 -07001// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Ken Mixter03403162010-08-18 15:23:16 -07005#include "crash-reporter/user_collector.h"
6
Ken Mixter777484c2010-07-23 16:22:44 -07007#include <grp.h> // For struct group.
8#include <pwd.h> // For struct passwd.
9#include <sys/types.h> // For getpwuid_r and getgrnam_r.
10
Chris Sosae4a86032010-06-16 17:08:34 -070011#include <string>
12
13#include "base/file_util.h"
14#include "base/logging.h"
15#include "base/string_util.h"
Ken Mixter03403162010-08-18 15:23:16 -070016#include "crash-reporter/system_logging.h"
Chris Sosae4a86032010-06-16 17:08:34 -070017
18// This procfs file is used to cause kernel core file writing to
19// instead pipe the core file into a user space process. See
20// core(5) man page.
21static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern";
Ken Mixter777484c2010-07-23 16:22:44 -070022static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md";
Ken Mixter7ac7a702010-08-13 15:43:34 -070023static const char kLeaveCoreFile[] = "/root/.leave_core";
Ken Mixter777484c2010-07-23 16:22:44 -070024
25const char *UserCollector::kUserId = "Uid:\t";
26const char *UserCollector::kGroupId = "Gid:\t";
Chris Sosae4a86032010-06-16 17:08:34 -070027
28UserCollector::UserCollector()
Ken Mixter777484c2010-07-23 16:22:44 -070029 : generate_diagnostics_(false),
30 core_pattern_file_(kCorePatternFile),
Ken Mixter03403162010-08-18 15:23:16 -070031 initialized_(false) {
Chris Sosae4a86032010-06-16 17:08:34 -070032}
33
34void UserCollector::Initialize(
35 UserCollector::CountCrashFunction count_crash_function,
36 const std::string &our_path,
37 UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function,
Ken Mixter777484c2010-07-23 16:22:44 -070038 SystemLogging *logger,
39 bool generate_diagnostics) {
Ken Mixter03403162010-08-18 15:23:16 -070040 CrashCollector::Initialize(count_crash_function,
41 is_feedback_allowed_function,
42 logger);
Chris Sosae4a86032010-06-16 17:08:34 -070043 our_path_ = our_path;
Chris Sosae4a86032010-06-16 17:08:34 -070044 initialized_ = true;
Ken Mixter777484c2010-07-23 16:22:44 -070045 generate_diagnostics_ = generate_diagnostics;
Chris Sosae4a86032010-06-16 17:08:34 -070046}
47
48UserCollector::~UserCollector() {
49}
50
51std::string UserCollector::GetPattern(bool enabled) const {
52 if (enabled) {
Ken Mixter777484c2010-07-23 16:22:44 -070053 return StringPrintf("|%s --signal=%%s --pid=%%p", our_path_.c_str());
Chris Sosae4a86032010-06-16 17:08:34 -070054 } else {
55 return "core";
56 }
57}
58
59bool UserCollector::SetUpInternal(bool enabled) {
60 CHECK(initialized_);
Ken Mixter03403162010-08-18 15:23:16 -070061 logger_->LogInfo("%s user crash handling",
62 enabled ? "Enabling" : "Disabling");
Chris Sosae4a86032010-06-16 17:08:34 -070063 std::string pattern = GetPattern(enabled);
64 if (file_util::WriteFile(FilePath(core_pattern_file_),
65 pattern.c_str(),
66 pattern.length()) !=
67 static_cast<int>(pattern.length())) {
68 logger_->LogError("Unable to write %s", core_pattern_file_.c_str());
69 return false;
70 }
71 return true;
72}
73
Ken Mixter777484c2010-07-23 16:22:44 -070074FilePath UserCollector::GetProcessPath(pid_t pid) {
75 return FilePath(StringPrintf("/proc/%d", pid));
76}
77
78bool UserCollector::GetSymlinkTarget(const FilePath &symlink,
79 FilePath *target) {
80 int max_size = 32;
81 scoped_array<char> buffer;
82 while (true) {
83 buffer.reset(new char[max_size + 1]);
84 ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size);
85 if (size < 0) {
86 return false;
87 }
88 buffer[size] = 0;
89 if (size == max_size) {
90 // Avoid overflow when doubling.
91 if (max_size * 2 > max_size) {
92 max_size *= 2;
93 continue;
94 } else {
95 return false;
96 }
97 }
98 break;
99 }
100
101 *target = FilePath(buffer.get());
102 return true;
103}
104
105bool UserCollector::GetExecutableBaseNameFromPid(uid_t pid,
106 std::string *base_name) {
107 FilePath target;
108 if (!GetSymlinkTarget(GetProcessPath(pid).Append("exe"), &target))
109 return false;
110 *base_name = target.BaseName().value();
111 return true;
112}
113
114bool UserCollector::GetIdFromStatus(const char *prefix,
115 IdKind kind,
116 const std::string &status_contents,
117 int *id) {
118 // From fs/proc/array.c:task_state(), this file contains:
119 // \nUid:\t<uid>\t<euid>\t<suid>\t<fsuid>\n
120 std::vector<std::string> status_lines;
121 SplitString(status_contents, '\n', &status_lines);
122 std::vector<std::string>::iterator line_iterator;
123 for (line_iterator = status_lines.begin();
124 line_iterator != status_lines.end();
125 ++line_iterator) {
126 if (line_iterator->find(prefix) == 0)
127 break;
128 }
129 if (line_iterator == status_lines.end()) {
130 return false;
131 }
132 std::string id_substring = line_iterator->substr(strlen(prefix),
133 std::string::npos);
134 std::vector<std::string> ids;
135 SplitString(id_substring, '\t', &ids);
136 if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) {
137 return false;
138 }
139 const char *number = ids[kind].c_str();
140 char *end_number = NULL;
141 *id = strtol(number, &end_number, 10);
142 if (*end_number != '\0')
143 return false;
144 return true;
145}
146
Ken Mixter777484c2010-07-23 16:22:44 -0700147bool UserCollector::CopyOffProcFiles(pid_t pid,
148 const FilePath &container_dir) {
149 if (!file_util::CreateDirectory(container_dir)) {
150 logger_->LogInfo("Could not create %s", container_dir.value().c_str());
151 return false;
152 }
153 FilePath process_path = GetProcessPath(pid);
154 if (!file_util::PathExists(process_path)) {
155 logger_->LogWarning("Path %s does not exist",
156 process_path.value().c_str());
157 return false;
158 }
159 static const char *proc_files[] = {
160 "auxv",
161 "cmdline",
162 "environ",
163 "maps",
164 "status"
165 };
166 for (unsigned i = 0; i < arraysize(proc_files); ++i) {
167 if (!file_util::CopyFile(process_path.Append(proc_files[i]),
168 container_dir.Append(proc_files[i]))) {
169 logger_->LogWarning("Could not copy %s file", proc_files[i]);
170 return false;
171 }
172 }
173 return true;
174}
175
Ken Mixter777484c2010-07-23 16:22:44 -0700176bool UserCollector::GetCreatedCrashDirectory(pid_t pid,
177 FilePath *crash_file_path) {
178 FilePath process_path = GetProcessPath(pid);
179 std::string status;
180 if (!file_util::ReadFileToString(process_path.Append("status"),
181 &status)) {
182 logger_->LogError("Could not read status file");
183 return false;
184 }
185 int process_euid;
186 if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) {
187 logger_->LogError("Could not find euid in status file");
188 return false;
189 }
Ken Mixter03403162010-08-18 15:23:16 -0700190 return GetCreatedCrashDirectoryByEuid(process_euid,
191 crash_file_path);
Ken Mixter777484c2010-07-23 16:22:44 -0700192}
193
194bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) {
195 // Copy off all stdin to a core file.
196 FilePath stdin_path("/dev/fd/0");
197 if (file_util::CopyFile(stdin_path, core_path)) {
198 return true;
199 }
200
201 logger_->LogError("Could not write core file");
202 // If the file system was full, make sure we remove any remnants.
203 file_util::Delete(core_path, false);
204 return false;
205}
206
207bool UserCollector::ConvertCoreToMinidump(const FilePath &core_path,
208 const FilePath &procfs_directory,
209 const FilePath &minidump_path,
210 const FilePath &temp_directory) {
211 // TODO(kmixter): Rewrite to use process_util once it's included in
212 // libchrome.
213 FilePath output_path = temp_directory.Append("output");
214 std::string core2md_command =
215 StringPrintf("\"%s\" \"%s\" \"%s\" \"%s\" > \"%s\" 2>&1",
216 kCoreToMinidumpConverterPath,
217 core_path.value().c_str(),
218 procfs_directory.value().c_str(),
219 minidump_path.value().c_str(),
220 output_path.value().c_str());
221 int errorlevel = system(core2md_command.c_str());
222
223 std::string output;
224 file_util::ReadFileToString(output_path, &output);
225 if (errorlevel != 0) {
226 logger_->LogInfo("Problem during %s [result=%d]: %s",
227 core2md_command.c_str(),
228 errorlevel,
229 output.c_str());
230 return false;
231 }
232
233 if (!file_util::PathExists(minidump_path)) {
234 logger_->LogError("Minidump file %s was not created",
235 minidump_path.value().c_str());
236 return false;
237 }
238 return true;
239}
240
241bool UserCollector::GenerateDiagnostics(pid_t pid,
242 const std::string &exec_name) {
243 FilePath container_dir("/tmp");
244 container_dir = container_dir.Append(
245 StringPrintf("crash_reporter.%d", pid));
246
247 if (!CopyOffProcFiles(pid, container_dir)) {
248 file_util::Delete(container_dir, true);
249 return false;
250 }
251
Ken Mixter03403162010-08-18 15:23:16 -0700252 FilePath crash_path;
253 if (!GetCreatedCrashDirectory(pid, &crash_path)) {
Ken Mixter777484c2010-07-23 16:22:44 -0700254 file_util::Delete(container_dir, true);
255 return false;
256 }
257 std::string dump_basename = FormatDumpBasename(exec_name, time(NULL), pid);
Ken Mixter03403162010-08-18 15:23:16 -0700258 FilePath core_path = crash_path.Append(
Ken Mixter777484c2010-07-23 16:22:44 -0700259 StringPrintf("%s.core", dump_basename.c_str()));
260
261 if (!CopyStdinToCoreFile(core_path)) {
262 file_util::Delete(container_dir, true);
263 return false;
264 }
265
Ken Mixter03403162010-08-18 15:23:16 -0700266 FilePath minidump_path = crash_path.Append(
Ken Mixter777484c2010-07-23 16:22:44 -0700267 StringPrintf("%s.dmp", dump_basename.c_str()));
268
269 bool conversion_result = true;
270 if (!ConvertCoreToMinidump(core_path,
271 container_dir, // procfs directory
272 minidump_path,
273 container_dir)) { // temporary directory
274 // Note we leave the container directory for inspection.
275 conversion_result = false;
276 }
277
278 if (conversion_result) {
279 logger_->LogInfo("Stored minidump to %s", minidump_path.value().c_str());
280 }
281
282 if (!file_util::PathExists(FilePath(kLeaveCoreFile))) {
283 file_util::Delete(core_path, false);
284 } else {
285 logger_->LogInfo("Leaving core file at %s", core_path.value().c_str());
286 }
287
288 return conversion_result;
289}
290
291bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) {
Chris Sosae4a86032010-06-16 17:08:34 -0700292 CHECK(initialized_);
Ken Mixter777484c2010-07-23 16:22:44 -0700293 std::string exec;
294 if (force_exec) {
295 exec.assign(force_exec);
296 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) {
297 // If for some reason we don't have the base name, avoid completely
298 // failing by indicating an unknown name.
299 exec = "unknown";
300 }
Chris Sosae4a86032010-06-16 17:08:34 -0700301 logger_->LogWarning("Received crash notification for %s[%d] sig %d",
302 exec.c_str(), pid, signal);
303
304 if (is_feedback_allowed_function_()) {
305 count_crash_function_();
Ken Mixter777484c2010-07-23 16:22:44 -0700306
Ken Mixter03403162010-08-18 15:23:16 -0700307 if (generate_diagnostics_) {
308 return GenerateDiagnostics(pid, exec);
309 }
Ken Mixter777484c2010-07-23 16:22:44 -0700310 }
311 return true;
Chris Sosae4a86032010-06-16 17:08:34 -0700312}