blob: bc414ab7a1ac8ac9acdad3d5b964fa1a909ccd0b [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 Mixter2953c3a2010-10-18 14:42:20 -07007#include <fcntl.h> // For creat.
Ken Mixter777484c2010-07-23 16:22:44 -07008#include <grp.h> // For struct group.
9#include <pwd.h> // For struct passwd.
Ken Mixter2953c3a2010-10-18 14:42:20 -070010#include <sys/types.h> // For getpwuid_r, getgrnam_r, WEXITSTATUS.
11#include <sys/wait.h> // For waitpid.
12#include <unistd.h> // For execv and fork.
Ken Mixter777484c2010-07-23 16:22:44 -070013
Chris Sosae4a86032010-06-16 17:08:34 -070014#include <string>
Ken Mixter2953c3a2010-10-18 14:42:20 -070015#include <vector>
Chris Sosae4a86032010-06-16 17:08:34 -070016
Ken Mixter2953c3a2010-10-18 14:42:20 -070017#include "base/eintr_wrapper.h"
Chris Sosae4a86032010-06-16 17:08:34 -070018#include "base/file_util.h"
19#include "base/logging.h"
20#include "base/string_util.h"
Ken Mixter03403162010-08-18 15:23:16 -070021#include "crash-reporter/system_logging.h"
Chris Sosae4a86032010-06-16 17:08:34 -070022
23// This procfs file is used to cause kernel core file writing to
24// instead pipe the core file into a user space process. See
25// core(5) man page.
26static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern";
Ken Mixter777484c2010-07-23 16:22:44 -070027static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md";
Ken Mixter7ac7a702010-08-13 15:43:34 -070028static const char kLeaveCoreFile[] = "/root/.leave_core";
Ken Mixter777484c2010-07-23 16:22:44 -070029
30const char *UserCollector::kUserId = "Uid:\t";
31const char *UserCollector::kGroupId = "Gid:\t";
Chris Sosae4a86032010-06-16 17:08:34 -070032
33UserCollector::UserCollector()
Ken Mixter777484c2010-07-23 16:22:44 -070034 : generate_diagnostics_(false),
35 core_pattern_file_(kCorePatternFile),
Ken Mixter03403162010-08-18 15:23:16 -070036 initialized_(false) {
Chris Sosae4a86032010-06-16 17:08:34 -070037}
38
39void UserCollector::Initialize(
40 UserCollector::CountCrashFunction count_crash_function,
41 const std::string &our_path,
42 UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function,
Ken Mixter777484c2010-07-23 16:22:44 -070043 SystemLogging *logger,
44 bool generate_diagnostics) {
Ken Mixter03403162010-08-18 15:23:16 -070045 CrashCollector::Initialize(count_crash_function,
46 is_feedback_allowed_function,
47 logger);
Chris Sosae4a86032010-06-16 17:08:34 -070048 our_path_ = our_path;
Chris Sosae4a86032010-06-16 17:08:34 -070049 initialized_ = true;
Ken Mixter777484c2010-07-23 16:22:44 -070050 generate_diagnostics_ = generate_diagnostics;
Chris Sosae4a86032010-06-16 17:08:34 -070051}
52
53UserCollector::~UserCollector() {
54}
55
56std::string UserCollector::GetPattern(bool enabled) const {
57 if (enabled) {
Ken Mixter777484c2010-07-23 16:22:44 -070058 return StringPrintf("|%s --signal=%%s --pid=%%p", our_path_.c_str());
Chris Sosae4a86032010-06-16 17:08:34 -070059 } else {
60 return "core";
61 }
62}
63
64bool UserCollector::SetUpInternal(bool enabled) {
65 CHECK(initialized_);
Ken Mixter03403162010-08-18 15:23:16 -070066 logger_->LogInfo("%s user crash handling",
67 enabled ? "Enabling" : "Disabling");
Chris Sosae4a86032010-06-16 17:08:34 -070068 std::string pattern = GetPattern(enabled);
69 if (file_util::WriteFile(FilePath(core_pattern_file_),
70 pattern.c_str(),
71 pattern.length()) !=
72 static_cast<int>(pattern.length())) {
73 logger_->LogError("Unable to write %s", core_pattern_file_.c_str());
74 return false;
75 }
76 return true;
77}
78
Ken Mixter777484c2010-07-23 16:22:44 -070079FilePath UserCollector::GetProcessPath(pid_t pid) {
80 return FilePath(StringPrintf("/proc/%d", pid));
81}
82
83bool UserCollector::GetSymlinkTarget(const FilePath &symlink,
84 FilePath *target) {
85 int max_size = 32;
86 scoped_array<char> buffer;
87 while (true) {
88 buffer.reset(new char[max_size + 1]);
89 ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size);
90 if (size < 0) {
91 return false;
92 }
93 buffer[size] = 0;
94 if (size == max_size) {
95 // Avoid overflow when doubling.
96 if (max_size * 2 > max_size) {
97 max_size *= 2;
98 continue;
99 } else {
100 return false;
101 }
102 }
103 break;
104 }
105
106 *target = FilePath(buffer.get());
107 return true;
108}
109
110bool UserCollector::GetExecutableBaseNameFromPid(uid_t pid,
111 std::string *base_name) {
112 FilePath target;
113 if (!GetSymlinkTarget(GetProcessPath(pid).Append("exe"), &target))
114 return false;
115 *base_name = target.BaseName().value();
116 return true;
117}
118
119bool UserCollector::GetIdFromStatus(const char *prefix,
120 IdKind kind,
121 const std::string &status_contents,
122 int *id) {
123 // From fs/proc/array.c:task_state(), this file contains:
124 // \nUid:\t<uid>\t<euid>\t<suid>\t<fsuid>\n
125 std::vector<std::string> status_lines;
126 SplitString(status_contents, '\n', &status_lines);
127 std::vector<std::string>::iterator line_iterator;
128 for (line_iterator = status_lines.begin();
129 line_iterator != status_lines.end();
130 ++line_iterator) {
131 if (line_iterator->find(prefix) == 0)
132 break;
133 }
134 if (line_iterator == status_lines.end()) {
135 return false;
136 }
137 std::string id_substring = line_iterator->substr(strlen(prefix),
138 std::string::npos);
139 std::vector<std::string> ids;
140 SplitString(id_substring, '\t', &ids);
141 if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) {
142 return false;
143 }
144 const char *number = ids[kind].c_str();
145 char *end_number = NULL;
146 *id = strtol(number, &end_number, 10);
147 if (*end_number != '\0')
148 return false;
149 return true;
150}
151
Ken Mixter777484c2010-07-23 16:22:44 -0700152bool UserCollector::CopyOffProcFiles(pid_t pid,
153 const FilePath &container_dir) {
154 if (!file_util::CreateDirectory(container_dir)) {
155 logger_->LogInfo("Could not create %s", container_dir.value().c_str());
156 return false;
157 }
158 FilePath process_path = GetProcessPath(pid);
159 if (!file_util::PathExists(process_path)) {
160 logger_->LogWarning("Path %s does not exist",
161 process_path.value().c_str());
162 return false;
163 }
164 static const char *proc_files[] = {
165 "auxv",
166 "cmdline",
167 "environ",
168 "maps",
169 "status"
170 };
171 for (unsigned i = 0; i < arraysize(proc_files); ++i) {
172 if (!file_util::CopyFile(process_path.Append(proc_files[i]),
173 container_dir.Append(proc_files[i]))) {
174 logger_->LogWarning("Could not copy %s file", proc_files[i]);
175 return false;
176 }
177 }
178 return true;
179}
180
Ken Mixter777484c2010-07-23 16:22:44 -0700181bool UserCollector::GetCreatedCrashDirectory(pid_t pid,
182 FilePath *crash_file_path) {
183 FilePath process_path = GetProcessPath(pid);
184 std::string status;
185 if (!file_util::ReadFileToString(process_path.Append("status"),
186 &status)) {
187 logger_->LogError("Could not read status file");
188 return false;
189 }
190 int process_euid;
191 if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) {
192 logger_->LogError("Could not find euid in status file");
193 return false;
194 }
Ken Mixter03403162010-08-18 15:23:16 -0700195 return GetCreatedCrashDirectoryByEuid(process_euid,
196 crash_file_path);
Ken Mixter777484c2010-07-23 16:22:44 -0700197}
198
199bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) {
200 // Copy off all stdin to a core file.
201 FilePath stdin_path("/dev/fd/0");
202 if (file_util::CopyFile(stdin_path, core_path)) {
203 return true;
204 }
205
206 logger_->LogError("Could not write core file");
207 // If the file system was full, make sure we remove any remnants.
208 file_util::Delete(core_path, false);
209 return false;
210}
211
Ken Mixter2953c3a2010-10-18 14:42:20 -0700212int UserCollector::ForkExecAndPipe(std::vector<const char *> &arguments,
213 const char *output_file) {
214 // Copy off a writeable version of arguments.
215 scoped_array<char*> argv(new char *[arguments.size() + 1]);
216 int total_args_size = 0;
217 for (size_t i = 0; i < arguments.size(); ++i) {
218 if (arguments[i] == NULL) {
219 logger_->LogError("Bad parameter");
220 return -1;
221 }
222 total_args_size += strlen(arguments[i]) + 1;
223 }
224 scoped_array<char> buffer(new char[total_args_size]);
225 char *buffer_pointer = &buffer[0];
226
227 for (size_t i = 0; i < arguments.size(); ++i) {
228 argv[i] = buffer_pointer;
229 strcpy(buffer_pointer, arguments[i]);
230 buffer_pointer += strlen(arguments[i]);
231 *buffer_pointer = '\0';
232 ++buffer_pointer;
233 }
234 argv[arguments.size()] = NULL;
235
236 int pid = fork();
237 if (pid < 0) {
238 logger_->LogError("Fork failed: %d", errno);
239 return -1;
240 }
241
242 if (pid == 0) {
243 int output_handle = creat(output_file, 0700);
244 if (output_handle < 0) {
245 logger_->LogError("Could not create %s: %d", output_file, errno);
246 // Avoid exit() to avoid atexit handlers from parent.
247 _exit(127);
248 }
249 dup2(output_handle, 1);
250 dup2(output_handle, 2);
251 execv(argv[0], &argv[0]);
252 logger_->LogError("Exec failed: %d", errno);
253 _exit(127);
254 }
255
256 int status = 0;
257 if (HANDLE_EINTR(waitpid(pid, &status, 0)) < 0) {
258 logger_->LogError("Problem waiting for pid: %d", errno);
259 return -1;
260 }
261 if (!WIFEXITED(status)) {
262 logger_->LogError("Process did not exit normally: %x", status);
263 return -1;
264 }
265 return WEXITSTATUS(status);
266}
267
Ken Mixter777484c2010-07-23 16:22:44 -0700268bool UserCollector::ConvertCoreToMinidump(const FilePath &core_path,
269 const FilePath &procfs_directory,
270 const FilePath &minidump_path,
271 const FilePath &temp_directory) {
Ken Mixter777484c2010-07-23 16:22:44 -0700272 FilePath output_path = temp_directory.Append("output");
Ken Mixter2953c3a2010-10-18 14:42:20 -0700273 std::vector<const char *> core2md_arguments;
274 core2md_arguments.push_back(kCoreToMinidumpConverterPath);
275 core2md_arguments.push_back(core_path.value().c_str());
276 core2md_arguments.push_back(procfs_directory.value().c_str());
277 core2md_arguments.push_back(minidump_path.value().c_str());
278
279 int errorlevel = ForkExecAndPipe(core2md_arguments,
280 output_path.value().c_str());
Ken Mixter777484c2010-07-23 16:22:44 -0700281
282 std::string output;
283 file_util::ReadFileToString(output_path, &output);
284 if (errorlevel != 0) {
285 logger_->LogInfo("Problem during %s [result=%d]: %s",
Ken Mixter2953c3a2010-10-18 14:42:20 -0700286 kCoreToMinidumpConverterPath,
Ken Mixter777484c2010-07-23 16:22:44 -0700287 errorlevel,
288 output.c_str());
289 return false;
290 }
291
292 if (!file_util::PathExists(minidump_path)) {
293 logger_->LogError("Minidump file %s was not created",
294 minidump_path.value().c_str());
295 return false;
296 }
297 return true;
298}
299
300bool UserCollector::GenerateDiagnostics(pid_t pid,
301 const std::string &exec_name) {
302 FilePath container_dir("/tmp");
303 container_dir = container_dir.Append(
304 StringPrintf("crash_reporter.%d", pid));
305
306 if (!CopyOffProcFiles(pid, container_dir)) {
307 file_util::Delete(container_dir, true);
308 return false;
309 }
310
Ken Mixter03403162010-08-18 15:23:16 -0700311 FilePath crash_path;
312 if (!GetCreatedCrashDirectory(pid, &crash_path)) {
Ken Mixter777484c2010-07-23 16:22:44 -0700313 file_util::Delete(container_dir, true);
314 return false;
315 }
316 std::string dump_basename = FormatDumpBasename(exec_name, time(NULL), pid);
Ken Mixter03403162010-08-18 15:23:16 -0700317 FilePath core_path = crash_path.Append(
Ken Mixter777484c2010-07-23 16:22:44 -0700318 StringPrintf("%s.core", dump_basename.c_str()));
319
320 if (!CopyStdinToCoreFile(core_path)) {
321 file_util::Delete(container_dir, true);
322 return false;
323 }
324
Ken Mixter03403162010-08-18 15:23:16 -0700325 FilePath minidump_path = crash_path.Append(
Ken Mixter777484c2010-07-23 16:22:44 -0700326 StringPrintf("%s.dmp", dump_basename.c_str()));
327
328 bool conversion_result = true;
329 if (!ConvertCoreToMinidump(core_path,
330 container_dir, // procfs directory
331 minidump_path,
332 container_dir)) { // temporary directory
333 // Note we leave the container directory for inspection.
334 conversion_result = false;
335 }
336
Ken Mixteree849c52010-09-30 15:30:10 -0700337 WriteCrashMetaData(
338 crash_path.Append(
339 StringPrintf("%s.meta", dump_basename.c_str())),
Ken Mixterc909b692010-10-18 12:26:05 -0700340 exec_name,
341 minidump_path.value());
Ken Mixteree849c52010-09-30 15:30:10 -0700342
Ken Mixter777484c2010-07-23 16:22:44 -0700343 if (conversion_result) {
344 logger_->LogInfo("Stored minidump to %s", minidump_path.value().c_str());
345 }
346
347 if (!file_util::PathExists(FilePath(kLeaveCoreFile))) {
348 file_util::Delete(core_path, false);
349 } else {
350 logger_->LogInfo("Leaving core file at %s", core_path.value().c_str());
351 }
352
353 return conversion_result;
354}
355
356bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) {
Chris Sosae4a86032010-06-16 17:08:34 -0700357 CHECK(initialized_);
Ken Mixter777484c2010-07-23 16:22:44 -0700358 std::string exec;
359 if (force_exec) {
360 exec.assign(force_exec);
361 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) {
362 // If for some reason we don't have the base name, avoid completely
363 // failing by indicating an unknown name.
364 exec = "unknown";
365 }
Ken Mixteree849c52010-09-30 15:30:10 -0700366 bool feedback = is_feedback_allowed_function_();
367 logger_->LogWarning("Received crash notification for %s[%d] sig %d (%s)",
368 exec.c_str(), pid, signal,
369 feedback ? "handling" : "ignoring");
Chris Sosae4a86032010-06-16 17:08:34 -0700370
Ken Mixteree849c52010-09-30 15:30:10 -0700371 if (feedback) {
Chris Sosae4a86032010-06-16 17:08:34 -0700372 count_crash_function_();
Ken Mixter777484c2010-07-23 16:22:44 -0700373
Ken Mixter03403162010-08-18 15:23:16 -0700374 if (generate_diagnostics_) {
375 return GenerateDiagnostics(pid, exec);
376 }
Ken Mixter777484c2010-07-23 16:22:44 -0700377 }
378 return true;
Chris Sosae4a86032010-06-16 17:08:34 -0700379}