blob: f9cb494009376efceda68b39f70c2996836ba704 [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"
Ken Mixter207694d2010-10-28 15:42:37 -070022#include "gflags/gflags.h"
Chris Sosae4a86032010-06-16 17:08:34 -070023
Ken Mixter207694d2010-10-28 15:42:37 -070024DEFINE_bool(core2md_failure_test, false, "Core2md failure test");
25DEFINE_bool(directory_failure_test, false, "Spool directory failure test");
26
27static const char kCollectionErrorSignature[] =
28 "crash_reporter-user-collection";
Chris Sosae4a86032010-06-16 17:08:34 -070029// This procfs file is used to cause kernel core file writing to
30// instead pipe the core file into a user space process. See
31// core(5) man page.
32static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern";
Ken Mixter777484c2010-07-23 16:22:44 -070033static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md";
Ken Mixter7ac7a702010-08-13 15:43:34 -070034static const char kLeaveCoreFile[] = "/root/.leave_core";
Ken Mixter777484c2010-07-23 16:22:44 -070035
36const char *UserCollector::kUserId = "Uid:\t";
37const char *UserCollector::kGroupId = "Gid:\t";
Chris Sosae4a86032010-06-16 17:08:34 -070038
39UserCollector::UserCollector()
Ken Mixter777484c2010-07-23 16:22:44 -070040 : generate_diagnostics_(false),
41 core_pattern_file_(kCorePatternFile),
Ken Mixter03403162010-08-18 15:23:16 -070042 initialized_(false) {
Chris Sosae4a86032010-06-16 17:08:34 -070043}
44
45void UserCollector::Initialize(
46 UserCollector::CountCrashFunction count_crash_function,
47 const std::string &our_path,
48 UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function,
Ken Mixter777484c2010-07-23 16:22:44 -070049 SystemLogging *logger,
50 bool generate_diagnostics) {
Ken Mixter03403162010-08-18 15:23:16 -070051 CrashCollector::Initialize(count_crash_function,
52 is_feedback_allowed_function,
53 logger);
Chris Sosae4a86032010-06-16 17:08:34 -070054 our_path_ = our_path;
Chris Sosae4a86032010-06-16 17:08:34 -070055 initialized_ = true;
Ken Mixter777484c2010-07-23 16:22:44 -070056 generate_diagnostics_ = generate_diagnostics;
Chris Sosae4a86032010-06-16 17:08:34 -070057}
58
59UserCollector::~UserCollector() {
60}
61
62std::string UserCollector::GetPattern(bool enabled) const {
63 if (enabled) {
Ken Mixter777484c2010-07-23 16:22:44 -070064 return StringPrintf("|%s --signal=%%s --pid=%%p", our_path_.c_str());
Chris Sosae4a86032010-06-16 17:08:34 -070065 } else {
66 return "core";
67 }
68}
69
70bool UserCollector::SetUpInternal(bool enabled) {
71 CHECK(initialized_);
Ken Mixter03403162010-08-18 15:23:16 -070072 logger_->LogInfo("%s user crash handling",
73 enabled ? "Enabling" : "Disabling");
Chris Sosae4a86032010-06-16 17:08:34 -070074 std::string pattern = GetPattern(enabled);
75 if (file_util::WriteFile(FilePath(core_pattern_file_),
76 pattern.c_str(),
77 pattern.length()) !=
78 static_cast<int>(pattern.length())) {
79 logger_->LogError("Unable to write %s", core_pattern_file_.c_str());
80 return false;
81 }
82 return true;
83}
84
Ken Mixter777484c2010-07-23 16:22:44 -070085FilePath UserCollector::GetProcessPath(pid_t pid) {
86 return FilePath(StringPrintf("/proc/%d", pid));
87}
88
89bool UserCollector::GetSymlinkTarget(const FilePath &symlink,
90 FilePath *target) {
91 int max_size = 32;
92 scoped_array<char> buffer;
93 while (true) {
94 buffer.reset(new char[max_size + 1]);
95 ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size);
96 if (size < 0) {
97 return false;
98 }
99 buffer[size] = 0;
100 if (size == max_size) {
101 // Avoid overflow when doubling.
102 if (max_size * 2 > max_size) {
103 max_size *= 2;
104 continue;
105 } else {
106 return false;
107 }
108 }
109 break;
110 }
111
112 *target = FilePath(buffer.get());
113 return true;
114}
115
116bool UserCollector::GetExecutableBaseNameFromPid(uid_t pid,
117 std::string *base_name) {
118 FilePath target;
119 if (!GetSymlinkTarget(GetProcessPath(pid).Append("exe"), &target))
120 return false;
121 *base_name = target.BaseName().value();
122 return true;
123}
124
125bool UserCollector::GetIdFromStatus(const char *prefix,
126 IdKind kind,
127 const std::string &status_contents,
128 int *id) {
129 // From fs/proc/array.c:task_state(), this file contains:
130 // \nUid:\t<uid>\t<euid>\t<suid>\t<fsuid>\n
131 std::vector<std::string> status_lines;
132 SplitString(status_contents, '\n', &status_lines);
133 std::vector<std::string>::iterator line_iterator;
134 for (line_iterator = status_lines.begin();
135 line_iterator != status_lines.end();
136 ++line_iterator) {
137 if (line_iterator->find(prefix) == 0)
138 break;
139 }
140 if (line_iterator == status_lines.end()) {
141 return false;
142 }
143 std::string id_substring = line_iterator->substr(strlen(prefix),
144 std::string::npos);
145 std::vector<std::string> ids;
146 SplitString(id_substring, '\t', &ids);
147 if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) {
148 return false;
149 }
150 const char *number = ids[kind].c_str();
151 char *end_number = NULL;
152 *id = strtol(number, &end_number, 10);
153 if (*end_number != '\0')
154 return false;
155 return true;
156}
157
Ken Mixter207694d2010-10-28 15:42:37 -0700158void UserCollector::LogCollectionError(const std::string &error_message) {
159 error_log_.append(error_message.c_str());
160 error_log_.append("\n");
161 logger_->LogError(error_message.c_str());
162}
163
164void UserCollector::EnqueueCollectionErrorLog(pid_t pid,
165 const std::string &exec) {
166 FilePath crash_path;
167 logger_->LogInfo("Writing conversion problems as separate crash report.");
168 if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, NULL)) {
169 logger_->LogError("Could not even get log directory; out of space?");
170 return;
171 }
172 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
173 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
174 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
175 file_util::WriteFile(log_path,
176 error_log_.data(),
177 error_log_.length());
178 AddCrashMetaData("sig", kCollectionErrorSignature);
179 WriteCrashMetaData(meta_path, exec, log_path.value());
180}
181
Ken Mixter777484c2010-07-23 16:22:44 -0700182bool UserCollector::CopyOffProcFiles(pid_t pid,
183 const FilePath &container_dir) {
184 if (!file_util::CreateDirectory(container_dir)) {
Ken Mixter207694d2010-10-28 15:42:37 -0700185 LogCollectionError(StringPrintf("Could not create %s",
186 container_dir.value().c_str()));
Ken Mixter777484c2010-07-23 16:22:44 -0700187 return false;
188 }
189 FilePath process_path = GetProcessPath(pid);
190 if (!file_util::PathExists(process_path)) {
Ken Mixter207694d2010-10-28 15:42:37 -0700191 LogCollectionError(StringPrintf("Path %s does not exist",
192 process_path.value().c_str()));
Ken Mixter777484c2010-07-23 16:22:44 -0700193 return false;
194 }
195 static const char *proc_files[] = {
196 "auxv",
197 "cmdline",
198 "environ",
199 "maps",
200 "status"
201 };
202 for (unsigned i = 0; i < arraysize(proc_files); ++i) {
203 if (!file_util::CopyFile(process_path.Append(proc_files[i]),
204 container_dir.Append(proc_files[i]))) {
Ken Mixter207694d2010-10-28 15:42:37 -0700205 LogCollectionError(StringPrintf("Could not copy %s file",
206 proc_files[i]));
Ken Mixter777484c2010-07-23 16:22:44 -0700207 return false;
208 }
209 }
210 return true;
211}
212
Ken Mixter777484c2010-07-23 16:22:44 -0700213bool UserCollector::GetCreatedCrashDirectory(pid_t pid,
Ken Mixter207694d2010-10-28 15:42:37 -0700214 FilePath *crash_file_path,
215 bool *out_of_capacity) {
Ken Mixter777484c2010-07-23 16:22:44 -0700216 FilePath process_path = GetProcessPath(pid);
217 std::string status;
Ken Mixter207694d2010-10-28 15:42:37 -0700218 if (FLAGS_directory_failure_test) {
219 LogCollectionError("Purposefully failing to create spool directory");
220 return false;
221 }
Ken Mixter777484c2010-07-23 16:22:44 -0700222 if (!file_util::ReadFileToString(process_path.Append("status"),
223 &status)) {
Ken Mixter207694d2010-10-28 15:42:37 -0700224 LogCollectionError("Could not read status file");
Ken Mixter777484c2010-07-23 16:22:44 -0700225 return false;
226 }
227 int process_euid;
228 if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) {
Ken Mixter207694d2010-10-28 15:42:37 -0700229 LogCollectionError("Could not find euid in status file");
Ken Mixter777484c2010-07-23 16:22:44 -0700230 return false;
231 }
Ken Mixter207694d2010-10-28 15:42:37 -0700232 if (!GetCreatedCrashDirectoryByEuid(process_euid,
233 crash_file_path,
234 out_of_capacity)) {
235 LogCollectionError("Could not create crash directory");
236 return false;
237 }
238 return true;
Ken Mixter777484c2010-07-23 16:22:44 -0700239}
240
241bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) {
242 // Copy off all stdin to a core file.
243 FilePath stdin_path("/dev/fd/0");
244 if (file_util::CopyFile(stdin_path, core_path)) {
245 return true;
246 }
247
Ken Mixter207694d2010-10-28 15:42:37 -0700248 LogCollectionError("Could not write core file");
Ken Mixter777484c2010-07-23 16:22:44 -0700249 // If the file system was full, make sure we remove any remnants.
250 file_util::Delete(core_path, false);
251 return false;
252}
253
Ken Mixter2953c3a2010-10-18 14:42:20 -0700254int UserCollector::ForkExecAndPipe(std::vector<const char *> &arguments,
255 const char *output_file) {
256 // Copy off a writeable version of arguments.
257 scoped_array<char*> argv(new char *[arguments.size() + 1]);
258 int total_args_size = 0;
259 for (size_t i = 0; i < arguments.size(); ++i) {
260 if (arguments[i] == NULL) {
261 logger_->LogError("Bad parameter");
262 return -1;
263 }
264 total_args_size += strlen(arguments[i]) + 1;
265 }
266 scoped_array<char> buffer(new char[total_args_size]);
267 char *buffer_pointer = &buffer[0];
268
269 for (size_t i = 0; i < arguments.size(); ++i) {
270 argv[i] = buffer_pointer;
271 strcpy(buffer_pointer, arguments[i]);
272 buffer_pointer += strlen(arguments[i]);
273 *buffer_pointer = '\0';
274 ++buffer_pointer;
275 }
276 argv[arguments.size()] = NULL;
277
278 int pid = fork();
279 if (pid < 0) {
280 logger_->LogError("Fork failed: %d", errno);
281 return -1;
282 }
283
284 if (pid == 0) {
285 int output_handle = creat(output_file, 0700);
286 if (output_handle < 0) {
287 logger_->LogError("Could not create %s: %d", output_file, errno);
288 // Avoid exit() to avoid atexit handlers from parent.
289 _exit(127);
290 }
291 dup2(output_handle, 1);
292 dup2(output_handle, 2);
293 execv(argv[0], &argv[0]);
294 logger_->LogError("Exec failed: %d", errno);
295 _exit(127);
296 }
297
298 int status = 0;
299 if (HANDLE_EINTR(waitpid(pid, &status, 0)) < 0) {
300 logger_->LogError("Problem waiting for pid: %d", errno);
301 return -1;
302 }
303 if (!WIFEXITED(status)) {
Ken Mixter207694d2010-10-28 15:42:37 -0700304 logger_->LogError("Process did not exit normally: %d", status);
Ken Mixter2953c3a2010-10-18 14:42:20 -0700305 return -1;
306 }
307 return WEXITSTATUS(status);
308}
309
Ken Mixter207694d2010-10-28 15:42:37 -0700310bool UserCollector::RunCoreToMinidump(const FilePath &core_path,
311 const FilePath &procfs_directory,
312 const FilePath &minidump_path,
313 const FilePath &temp_directory) {
Ken Mixter777484c2010-07-23 16:22:44 -0700314 FilePath output_path = temp_directory.Append("output");
Ken Mixter2953c3a2010-10-18 14:42:20 -0700315 std::vector<const char *> core2md_arguments;
316 core2md_arguments.push_back(kCoreToMinidumpConverterPath);
317 core2md_arguments.push_back(core_path.value().c_str());
318 core2md_arguments.push_back(procfs_directory.value().c_str());
319 core2md_arguments.push_back(minidump_path.value().c_str());
320
Ken Mixter207694d2010-10-28 15:42:37 -0700321 if (FLAGS_core2md_failure_test) {
322 // To test how core2md errors are propagaged, cause an error
323 // by forgetting a required argument.
324 core2md_arguments.pop_back();
325 }
326
Ken Mixter2953c3a2010-10-18 14:42:20 -0700327 int errorlevel = ForkExecAndPipe(core2md_arguments,
328 output_path.value().c_str());
Ken Mixter777484c2010-07-23 16:22:44 -0700329
330 std::string output;
331 file_util::ReadFileToString(output_path, &output);
332 if (errorlevel != 0) {
Ken Mixter207694d2010-10-28 15:42:37 -0700333 LogCollectionError(StringPrintf("Problem during %s [result=%d]: %s",
334 kCoreToMinidumpConverterPath,
335 errorlevel,
336 output.c_str()));
Ken Mixter777484c2010-07-23 16:22:44 -0700337 return false;
338 }
339
340 if (!file_util::PathExists(minidump_path)) {
Ken Mixter207694d2010-10-28 15:42:37 -0700341 LogCollectionError(StringPrintf("Minidump file %s was not created",
342 minidump_path.value().c_str()));
Ken Mixter777484c2010-07-23 16:22:44 -0700343 return false;
344 }
345 return true;
346}
347
Ken Mixter207694d2010-10-28 15:42:37 -0700348bool UserCollector::ConvertCoreToMinidump(pid_t pid,
349 const FilePath &container_dir,
350 const FilePath &core_path,
351 const FilePath &minidump_path) {
Ken Mixter777484c2010-07-23 16:22:44 -0700352 if (!CopyOffProcFiles(pid, container_dir)) {
Ken Mixter777484c2010-07-23 16:22:44 -0700353 return false;
354 }
355
Ken Mixter777484c2010-07-23 16:22:44 -0700356 if (!CopyStdinToCoreFile(core_path)) {
Ken Mixter777484c2010-07-23 16:22:44 -0700357 return false;
358 }
359
Ken Mixter207694d2010-10-28 15:42:37 -0700360 bool conversion_result = RunCoreToMinidump(
361 core_path,
362 container_dir, // procfs directory
363 minidump_path,
364 container_dir); // temporary directory
Ken Mixteree849c52010-09-30 15:30:10 -0700365
Ken Mixter777484c2010-07-23 16:22:44 -0700366 if (conversion_result) {
367 logger_->LogInfo("Stored minidump to %s", minidump_path.value().c_str());
368 }
369
Ken Mixter207694d2010-10-28 15:42:37 -0700370 return conversion_result;
371}
372
373bool UserCollector::ConvertAndEnqueueCrash(int pid,
374 const std::string &exec,
375 bool *out_of_capacity) {
376 FilePath crash_path;
377 if (!GetCreatedCrashDirectory(pid, &crash_path, out_of_capacity)) {
378 LogCollectionError("Unable to find/create process-specific crash path");
379 return false;
380 }
381
382 // Directory like /tmp/crash_reporter.1234 which contains the
383 // procfs entries and other temporary files used during conversion.
384 FilePath container_dir = FilePath("/tmp").Append(
385 StringPrintf("crash_reporter.%d", pid));
386 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
387 FilePath core_path = GetCrashPath(crash_path, dump_basename, "core");
388 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
389 FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp");
390
391 if (!ConvertCoreToMinidump(pid, container_dir, core_path,
392 minidump_path)) {
393 logger_->LogInfo("Leaving core file at %s due to conversion error",
394 core_path.value().c_str());
395 return false;
396 }
397
398 // Here we commit to sending this file. We must not return false
399 // after this point or we will generate a log report as well as a
400 // crash report.
401 WriteCrashMetaData(meta_path,
402 exec,
403 minidump_path.value());
404
Ken Mixter777484c2010-07-23 16:22:44 -0700405 if (!file_util::PathExists(FilePath(kLeaveCoreFile))) {
406 file_util::Delete(core_path, false);
407 } else {
Ken Mixter207694d2010-10-28 15:42:37 -0700408 logger_->LogInfo("Leaving core file at %s due to developer image",
409 core_path.value().c_str());
Ken Mixter777484c2010-07-23 16:22:44 -0700410 }
411
Ken Mixter207694d2010-10-28 15:42:37 -0700412 file_util::Delete(container_dir, true);
413 return true;
Ken Mixter777484c2010-07-23 16:22:44 -0700414}
415
416bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) {
Chris Sosae4a86032010-06-16 17:08:34 -0700417 CHECK(initialized_);
Ken Mixter777484c2010-07-23 16:22:44 -0700418 std::string exec;
419 if (force_exec) {
420 exec.assign(force_exec);
421 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) {
422 // If for some reason we don't have the base name, avoid completely
423 // failing by indicating an unknown name.
424 exec = "unknown";
425 }
Ken Mixteree849c52010-09-30 15:30:10 -0700426 bool feedback = is_feedback_allowed_function_();
427 logger_->LogWarning("Received crash notification for %s[%d] sig %d (%s)",
428 exec.c_str(), pid, signal,
Ken Mixter207694d2010-10-28 15:42:37 -0700429 feedback ? "handling" : "ignoring - no consent");
Chris Sosae4a86032010-06-16 17:08:34 -0700430
Ken Mixteree849c52010-09-30 15:30:10 -0700431 if (feedback) {
Chris Sosae4a86032010-06-16 17:08:34 -0700432 count_crash_function_();
Ken Mixter777484c2010-07-23 16:22:44 -0700433
Ken Mixter03403162010-08-18 15:23:16 -0700434 if (generate_diagnostics_) {
Ken Mixter207694d2010-10-28 15:42:37 -0700435 bool out_of_capacity = false;
436 if (!ConvertAndEnqueueCrash(pid, exec, &out_of_capacity)) {
437 if (!out_of_capacity)
438 EnqueueCollectionErrorLog(pid, exec);
439 return false;
440 }
Ken Mixter03403162010-08-18 15:23:16 -0700441 }
Ken Mixter777484c2010-07-23 16:22:44 -0700442 }
Ken Mixter207694d2010-10-28 15:42:37 -0700443
Ken Mixter777484c2010-07-23 16:22:44 -0700444 return true;
Chris Sosae4a86032010-06-16 17:08:34 -0700445}