| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 1 // 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "crash-reporter/user_collector.h" | 5 #include "crash-reporter/user_collector.h" |
| 6 | 6 |
| 7 #include <fcntl.h> // For creat. | 7 #include <fcntl.h> // For creat. |
| 8 #include <grp.h> // For struct group. | 8 #include <grp.h> // For struct group. |
| 9 #include <pwd.h> // For struct passwd. | 9 #include <pwd.h> // For struct passwd. |
| 10 #include <sys/types.h> // For getpwuid_r, getgrnam_r, WEXITSTATUS. | 10 #include <sys/types.h> // For getpwuid_r, getgrnam_r, WEXITSTATUS. |
| 11 #include <sys/wait.h> // For waitpid. | 11 #include <sys/wait.h> // For waitpid. |
| 12 #include <unistd.h> // For execv and fork. | 12 #include <unistd.h> // For execv and fork. |
| 13 | 13 |
| 14 #include <string> | 14 #include <string> |
| 15 #include <vector> | 15 #include <vector> |
| 16 | 16 |
| 17 #include "base/eintr_wrapper.h" | 17 #include "base/eintr_wrapper.h" |
| 18 #include "base/file_util.h" | 18 #include "base/file_util.h" |
| 19 #include "base/logging.h" | 19 #include "base/logging.h" |
| 20 #include "base/string_util.h" | 20 #include "base/string_util.h" |
| 21 #include "crash-reporter/system_logging.h" | 21 #include "crash-reporter/system_logging.h" |
| 22 #include "gflags/gflags.h" |
| 22 | 23 |
| 24 DEFINE_bool(core2md_failure_test, false, "Core2md failure test"); |
| 25 DEFINE_bool(directory_failure_test, false, "Spool directory failure test"); |
| 26 |
| 27 static const char kCollectionErrorSignature[] = |
| 28 "crash_reporter-user-collection"; |
| 23 // This procfs file is used to cause kernel core file writing to | 29 // This procfs file is used to cause kernel core file writing to |
| 24 // instead pipe the core file into a user space process. See | 30 // instead pipe the core file into a user space process. See |
| 25 // core(5) man page. | 31 // core(5) man page. |
| 26 static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern"; | 32 static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern"; |
| 27 static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md"; | 33 static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md"; |
| 28 static const char kLeaveCoreFile[] = "/root/.leave_core"; | 34 static const char kLeaveCoreFile[] = "/root/.leave_core"; |
| 29 | 35 |
| 30 const char *UserCollector::kUserId = "Uid:\t"; | 36 const char *UserCollector::kUserId = "Uid:\t"; |
| 31 const char *UserCollector::kGroupId = "Gid:\t"; | 37 const char *UserCollector::kGroupId = "Gid:\t"; |
| 32 | 38 |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 142 return false; | 148 return false; |
| 143 } | 149 } |
| 144 const char *number = ids[kind].c_str(); | 150 const char *number = ids[kind].c_str(); |
| 145 char *end_number = NULL; | 151 char *end_number = NULL; |
| 146 *id = strtol(number, &end_number, 10); | 152 *id = strtol(number, &end_number, 10); |
| 147 if (*end_number != '\0') | 153 if (*end_number != '\0') |
| 148 return false; | 154 return false; |
| 149 return true; | 155 return true; |
| 150 } | 156 } |
| 151 | 157 |
| 158 void 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 |
| 164 void 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 |
| 152 bool UserCollector::CopyOffProcFiles(pid_t pid, | 182 bool UserCollector::CopyOffProcFiles(pid_t pid, |
| 153 const FilePath &container_dir) { | 183 const FilePath &container_dir) { |
| 154 if (!file_util::CreateDirectory(container_dir)) { | 184 if (!file_util::CreateDirectory(container_dir)) { |
| 155 logger_->LogInfo("Could not create %s", container_dir.value().c_str()); | 185 LogCollectionError(StringPrintf("Could not create %s", |
| 186 container_dir.value().c_str())); |
| 156 return false; | 187 return false; |
| 157 } | 188 } |
| 158 FilePath process_path = GetProcessPath(pid); | 189 FilePath process_path = GetProcessPath(pid); |
| 159 if (!file_util::PathExists(process_path)) { | 190 if (!file_util::PathExists(process_path)) { |
| 160 logger_->LogWarning("Path %s does not exist", | 191 LogCollectionError(StringPrintf("Path %s does not exist", |
| 161 process_path.value().c_str()); | 192 process_path.value().c_str())); |
| 162 return false; | 193 return false; |
| 163 } | 194 } |
| 164 static const char *proc_files[] = { | 195 static const char *proc_files[] = { |
| 165 "auxv", | 196 "auxv", |
| 166 "cmdline", | 197 "cmdline", |
| 167 "environ", | 198 "environ", |
| 168 "maps", | 199 "maps", |
| 169 "status" | 200 "status" |
| 170 }; | 201 }; |
| 171 for (unsigned i = 0; i < arraysize(proc_files); ++i) { | 202 for (unsigned i = 0; i < arraysize(proc_files); ++i) { |
| 172 if (!file_util::CopyFile(process_path.Append(proc_files[i]), | 203 if (!file_util::CopyFile(process_path.Append(proc_files[i]), |
| 173 container_dir.Append(proc_files[i]))) { | 204 container_dir.Append(proc_files[i]))) { |
| 174 logger_->LogWarning("Could not copy %s file", proc_files[i]); | 205 LogCollectionError(StringPrintf("Could not copy %s file", |
| 206 proc_files[i])); |
| 175 return false; | 207 return false; |
| 176 } | 208 } |
| 177 } | 209 } |
| 178 return true; | 210 return true; |
| 179 } | 211 } |
| 180 | 212 |
| 181 bool UserCollector::GetCreatedCrashDirectory(pid_t pid, | 213 bool UserCollector::GetCreatedCrashDirectory(pid_t pid, |
| 182 FilePath *crash_file_path) { | 214 FilePath *crash_file_path, |
| 215 bool *out_of_capacity) { |
| 183 FilePath process_path = GetProcessPath(pid); | 216 FilePath process_path = GetProcessPath(pid); |
| 184 std::string status; | 217 std::string status; |
| 218 if (FLAGS_directory_failure_test) { |
| 219 LogCollectionError("Purposefully failing to create spool directory"); |
| 220 return false; |
| 221 } |
| 185 if (!file_util::ReadFileToString(process_path.Append("status"), | 222 if (!file_util::ReadFileToString(process_path.Append("status"), |
| 186 &status)) { | 223 &status)) { |
| 187 logger_->LogError("Could not read status file"); | 224 LogCollectionError("Could not read status file"); |
| 188 return false; | 225 return false; |
| 189 } | 226 } |
| 190 int process_euid; | 227 int process_euid; |
| 191 if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) { | 228 if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) { |
| 192 logger_->LogError("Could not find euid in status file"); | 229 LogCollectionError("Could not find euid in status file"); |
| 193 return false; | 230 return false; |
| 194 } | 231 } |
| 195 return GetCreatedCrashDirectoryByEuid(process_euid, | 232 if (!GetCreatedCrashDirectoryByEuid(process_euid, |
| 196 crash_file_path); | 233 crash_file_path, |
| 234 out_of_capacity)) { |
| 235 LogCollectionError("Could not create crash directory"); |
| 236 return false; |
| 237 } |
| 238 return true; |
| 197 } | 239 } |
| 198 | 240 |
| 199 bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) { | 241 bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) { |
| 200 // Copy off all stdin to a core file. | 242 // Copy off all stdin to a core file. |
| 201 FilePath stdin_path("/dev/fd/0"); | 243 FilePath stdin_path("/dev/fd/0"); |
| 202 if (file_util::CopyFile(stdin_path, core_path)) { | 244 if (file_util::CopyFile(stdin_path, core_path)) { |
| 203 return true; | 245 return true; |
| 204 } | 246 } |
| 205 | 247 |
| 206 logger_->LogError("Could not write core file"); | 248 LogCollectionError("Could not write core file"); |
| 207 // If the file system was full, make sure we remove any remnants. | 249 // If the file system was full, make sure we remove any remnants. |
| 208 file_util::Delete(core_path, false); | 250 file_util::Delete(core_path, false); |
| 209 return false; | 251 return false; |
| 210 } | 252 } |
| 211 | 253 |
| 212 int UserCollector::ForkExecAndPipe(std::vector<const char *> &arguments, | 254 int UserCollector::ForkExecAndPipe(std::vector<const char *> &arguments, |
| 213 const char *output_file) { | 255 const char *output_file) { |
| 214 // Copy off a writeable version of arguments. | 256 // Copy off a writeable version of arguments. |
| 215 scoped_array<char*> argv(new char *[arguments.size() + 1]); | 257 scoped_array<char*> argv(new char *[arguments.size() + 1]); |
| 216 int total_args_size = 0; | 258 int total_args_size = 0; |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 252 logger_->LogError("Exec failed: %d", errno); | 294 logger_->LogError("Exec failed: %d", errno); |
| 253 _exit(127); | 295 _exit(127); |
| 254 } | 296 } |
| 255 | 297 |
| 256 int status = 0; | 298 int status = 0; |
| 257 if (HANDLE_EINTR(waitpid(pid, &status, 0)) < 0) { | 299 if (HANDLE_EINTR(waitpid(pid, &status, 0)) < 0) { |
| 258 logger_->LogError("Problem waiting for pid: %d", errno); | 300 logger_->LogError("Problem waiting for pid: %d", errno); |
| 259 return -1; | 301 return -1; |
| 260 } | 302 } |
| 261 if (!WIFEXITED(status)) { | 303 if (!WIFEXITED(status)) { |
| 262 logger_->LogError("Process did not exit normally: %x", status); | 304 logger_->LogError("Process did not exit normally: %d", status); |
| 263 return -1; | 305 return -1; |
| 264 } | 306 } |
| 265 return WEXITSTATUS(status); | 307 return WEXITSTATUS(status); |
| 266 } | 308 } |
| 267 | 309 |
| 268 bool UserCollector::ConvertCoreToMinidump(const FilePath &core_path, | 310 bool UserCollector::RunCoreToMinidump(const FilePath &core_path, |
| 269 const FilePath &procfs_directory, | 311 const FilePath &procfs_directory, |
| 270 const FilePath &minidump_path, | 312 const FilePath &minidump_path, |
| 271 const FilePath &temp_directory) { | 313 const FilePath &temp_directory) { |
| 272 FilePath output_path = temp_directory.Append("output"); | 314 FilePath output_path = temp_directory.Append("output"); |
| 273 std::vector<const char *> core2md_arguments; | 315 std::vector<const char *> core2md_arguments; |
| 274 core2md_arguments.push_back(kCoreToMinidumpConverterPath); | 316 core2md_arguments.push_back(kCoreToMinidumpConverterPath); |
| 275 core2md_arguments.push_back(core_path.value().c_str()); | 317 core2md_arguments.push_back(core_path.value().c_str()); |
| 276 core2md_arguments.push_back(procfs_directory.value().c_str()); | 318 core2md_arguments.push_back(procfs_directory.value().c_str()); |
| 277 core2md_arguments.push_back(minidump_path.value().c_str()); | 319 core2md_arguments.push_back(minidump_path.value().c_str()); |
| 278 | 320 |
| 321 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 |
| 279 int errorlevel = ForkExecAndPipe(core2md_arguments, | 327 int errorlevel = ForkExecAndPipe(core2md_arguments, |
| 280 output_path.value().c_str()); | 328 output_path.value().c_str()); |
| 281 | 329 |
| 282 std::string output; | 330 std::string output; |
| 283 file_util::ReadFileToString(output_path, &output); | 331 file_util::ReadFileToString(output_path, &output); |
| 284 if (errorlevel != 0) { | 332 if (errorlevel != 0) { |
| 285 logger_->LogInfo("Problem during %s [result=%d]: %s", | 333 LogCollectionError(StringPrintf("Problem during %s [result=%d]: %s", |
| 286 kCoreToMinidumpConverterPath, | 334 kCoreToMinidumpConverterPath, |
| 287 errorlevel, | 335 errorlevel, |
| 288 output.c_str()); | 336 output.c_str())); |
| 289 return false; | 337 return false; |
| 290 } | 338 } |
| 291 | 339 |
| 292 if (!file_util::PathExists(minidump_path)) { | 340 if (!file_util::PathExists(minidump_path)) { |
| 293 logger_->LogError("Minidump file %s was not created", | 341 LogCollectionError(StringPrintf("Minidump file %s was not created", |
| 294 minidump_path.value().c_str()); | 342 minidump_path.value().c_str())); |
| 295 return false; | 343 return false; |
| 296 } | 344 } |
| 297 return true; | 345 return true; |
| 298 } | 346 } |
| 299 | 347 |
| 300 bool UserCollector::GenerateDiagnostics(pid_t pid, | 348 bool UserCollector::ConvertCoreToMinidump(pid_t pid, |
| 301 const std::string &exec_name) { | 349 const FilePath &container_dir, |
| 302 FilePath container_dir("/tmp"); | 350 const FilePath &core_path, |
| 303 container_dir = container_dir.Append( | 351 const FilePath &minidump_path) { |
| 304 StringPrintf("crash_reporter.%d", pid)); | |
| 305 | |
| 306 if (!CopyOffProcFiles(pid, container_dir)) { | 352 if (!CopyOffProcFiles(pid, container_dir)) { |
| 307 file_util::Delete(container_dir, true); | |
| 308 return false; | 353 return false; |
| 309 } | 354 } |
| 310 | 355 |
| 311 FilePath crash_path; | |
| 312 if (!GetCreatedCrashDirectory(pid, &crash_path)) { | |
| 313 file_util::Delete(container_dir, true); | |
| 314 return false; | |
| 315 } | |
| 316 std::string dump_basename = FormatDumpBasename(exec_name, time(NULL), pid); | |
| 317 FilePath core_path = crash_path.Append( | |
| 318 StringPrintf("%s.core", dump_basename.c_str())); | |
| 319 | |
| 320 if (!CopyStdinToCoreFile(core_path)) { | 356 if (!CopyStdinToCoreFile(core_path)) { |
| 321 file_util::Delete(container_dir, true); | |
| 322 return false; | 357 return false; |
| 323 } | 358 } |
| 324 | 359 |
| 325 FilePath minidump_path = crash_path.Append( | 360 bool conversion_result = RunCoreToMinidump( |
| 326 StringPrintf("%s.dmp", dump_basename.c_str())); | 361 core_path, |
| 327 | 362 container_dir, // procfs directory |
| 328 bool conversion_result = true; | 363 minidump_path, |
| 329 if (!ConvertCoreToMinidump(core_path, | 364 container_dir); // temporary directory |
| 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 } else { | |
| 336 file_util::Delete(container_dir, true); | |
| 337 } | |
| 338 | |
| 339 WriteCrashMetaData( | |
| 340 crash_path.Append( | |
| 341 StringPrintf("%s.meta", dump_basename.c_str())), | |
| 342 exec_name, | |
| 343 minidump_path.value()); | |
| 344 | 365 |
| 345 if (conversion_result) { | 366 if (conversion_result) { |
| 346 logger_->LogInfo("Stored minidump to %s", minidump_path.value().c_str()); | 367 logger_->LogInfo("Stored minidump to %s", minidump_path.value().c_str()); |
| 347 } | 368 } |
| 348 | 369 |
| 370 return conversion_result; |
| 371 } |
| 372 |
| 373 bool 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 |
| 349 if (!file_util::PathExists(FilePath(kLeaveCoreFile))) { | 405 if (!file_util::PathExists(FilePath(kLeaveCoreFile))) { |
| 350 file_util::Delete(core_path, false); | 406 file_util::Delete(core_path, false); |
| 351 } else { | 407 } else { |
| 352 logger_->LogInfo("Leaving core file at %s", core_path.value().c_str()); | 408 logger_->LogInfo("Leaving core file at %s due to developer image", |
| 409 core_path.value().c_str()); |
| 353 } | 410 } |
| 354 | 411 |
| 355 return conversion_result; | 412 file_util::Delete(container_dir, true); |
| 413 return true; |
| 356 } | 414 } |
| 357 | 415 |
| 358 bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) { | 416 bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) { |
| 359 CHECK(initialized_); | 417 CHECK(initialized_); |
| 360 std::string exec; | 418 std::string exec; |
| 361 if (force_exec) { | 419 if (force_exec) { |
| 362 exec.assign(force_exec); | 420 exec.assign(force_exec); |
| 363 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) { | 421 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) { |
| 364 // If for some reason we don't have the base name, avoid completely | 422 // If for some reason we don't have the base name, avoid completely |
| 365 // failing by indicating an unknown name. | 423 // failing by indicating an unknown name. |
| 366 exec = "unknown"; | 424 exec = "unknown"; |
| 367 } | 425 } |
| 368 bool feedback = is_feedback_allowed_function_(); | 426 bool feedback = is_feedback_allowed_function_(); |
| 369 logger_->LogWarning("Received crash notification for %s[%d] sig %d (%s)", | 427 logger_->LogWarning("Received crash notification for %s[%d] sig %d (%s)", |
| 370 exec.c_str(), pid, signal, | 428 exec.c_str(), pid, signal, |
| 371 feedback ? "handling" : "ignoring"); | 429 feedback ? "handling" : "ignoring - no consent"); |
| 372 | 430 |
| 373 if (feedback) { | 431 if (feedback) { |
| 374 count_crash_function_(); | 432 count_crash_function_(); |
| 375 | 433 |
| 376 if (generate_diagnostics_) { | 434 if (generate_diagnostics_) { |
| 377 return GenerateDiagnostics(pid, exec); | 435 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 } |
| 378 } | 441 } |
| 379 } | 442 } |
| 443 |
| 380 return true; | 444 return true; |
| 381 } | 445 } |
| OLD | NEW |