| 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" |
| 6 |
| 5 #include <grp.h> // For struct group. | 7 #include <grp.h> // For struct group. |
| 6 #include <pwd.h> // For struct passwd. | 8 #include <pwd.h> // For struct passwd. |
| 7 #include <sys/types.h> // For getpwuid_r and getgrnam_r. | 9 #include <sys/types.h> // For getpwuid_r and getgrnam_r. |
| 8 | 10 |
| 9 #include <string> | 11 #include <string> |
| 10 | 12 |
| 11 #include "base/file_util.h" | 13 #include "base/file_util.h" |
| 12 #include "base/logging.h" | 14 #include "base/logging.h" |
| 13 #include "base/string_util.h" | 15 #include "base/string_util.h" |
| 14 #include "crash-reporter/user_collector.h" | 16 #include "crash-reporter/system_logging.h" |
| 15 #include "metrics/metrics_library.h" | |
| 16 | 17 |
| 17 // This procfs file is used to cause kernel core file writing to | 18 // This procfs file is used to cause kernel core file writing to |
| 18 // instead pipe the core file into a user space process. See | 19 // instead pipe the core file into a user space process. See |
| 19 // core(5) man page. | 20 // core(5) man page. |
| 20 static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern"; | 21 static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern"; |
| 21 static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md"; | 22 static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md"; |
| 22 static const char kDefaultUserName[] = "chronos"; | |
| 23 static const char kLeaveCoreFile[] = "/root/.leave_core"; | 23 static const char kLeaveCoreFile[] = "/root/.leave_core"; |
| 24 static const char kSystemCrashPath[] = "/var/spool/crash"; | |
| 25 static const char kUserCrashPath[] = "/home/chronos/user/crash"; | |
| 26 | |
| 27 // Directory mode of the user crash spool directory. | |
| 28 static const mode_t kUserCrashPathMode = 0755; | |
| 29 | |
| 30 // Directory mode of the system crash spool directory. | |
| 31 static const mode_t kSystemCrashPathMode = 01755; | |
| 32 | |
| 33 static const uid_t kRootOwner = 0; | |
| 34 static const uid_t kRootGroup = 0; | |
| 35 | 24 |
| 36 const char *UserCollector::kUserId = "Uid:\t"; | 25 const char *UserCollector::kUserId = "Uid:\t"; |
| 37 const char *UserCollector::kGroupId = "Gid:\t"; | 26 const char *UserCollector::kGroupId = "Gid:\t"; |
| 38 | 27 |
| 39 UserCollector::UserCollector() | 28 UserCollector::UserCollector() |
| 40 : generate_diagnostics_(false), | 29 : generate_diagnostics_(false), |
| 41 core_pattern_file_(kCorePatternFile), | 30 core_pattern_file_(kCorePatternFile), |
| 42 count_crash_function_(NULL), | 31 initialized_(false) { |
| 43 initialized_(false), | |
| 44 is_feedback_allowed_function_(NULL), | |
| 45 logger_(NULL) { | |
| 46 } | 32 } |
| 47 | 33 |
| 48 void UserCollector::Initialize( | 34 void UserCollector::Initialize( |
| 49 UserCollector::CountCrashFunction count_crash_function, | 35 UserCollector::CountCrashFunction count_crash_function, |
| 50 const std::string &our_path, | 36 const std::string &our_path, |
| 51 UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function, | 37 UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function, |
| 52 SystemLogging *logger, | 38 SystemLogging *logger, |
| 53 bool generate_diagnostics) { | 39 bool generate_diagnostics) { |
| 54 CHECK(count_crash_function != NULL); | 40 CrashCollector::Initialize(count_crash_function, |
| 55 CHECK(is_feedback_allowed_function != NULL); | 41 is_feedback_allowed_function, |
| 56 CHECK(logger != NULL); | 42 logger); |
| 57 | |
| 58 count_crash_function_ = count_crash_function; | |
| 59 our_path_ = our_path; | 43 our_path_ = our_path; |
| 60 is_feedback_allowed_function_ = is_feedback_allowed_function; | |
| 61 logger_ = logger; | |
| 62 initialized_ = true; | 44 initialized_ = true; |
| 63 generate_diagnostics_ = generate_diagnostics; | 45 generate_diagnostics_ = generate_diagnostics; |
| 64 } | 46 } |
| 65 | 47 |
| 66 UserCollector::~UserCollector() { | 48 UserCollector::~UserCollector() { |
| 67 } | 49 } |
| 68 | 50 |
| 69 std::string UserCollector::GetPattern(bool enabled) const { | 51 std::string UserCollector::GetPattern(bool enabled) const { |
| 70 if (enabled) { | 52 if (enabled) { |
| 71 return StringPrintf("|%s --signal=%%s --pid=%%p", our_path_.c_str()); | 53 return StringPrintf("|%s --signal=%%s --pid=%%p", our_path_.c_str()); |
| 72 } else { | 54 } else { |
| 73 return "core"; | 55 return "core"; |
| 74 } | 56 } |
| 75 } | 57 } |
| 76 | 58 |
| 77 bool UserCollector::SetUpInternal(bool enabled) { | 59 bool UserCollector::SetUpInternal(bool enabled) { |
| 78 CHECK(initialized_); | 60 CHECK(initialized_); |
| 79 logger_->LogInfo("%s crash handling", enabled ? "Enabling" : "Disabling"); | 61 logger_->LogInfo("%s user crash handling", |
| 62 enabled ? "Enabling" : "Disabling"); |
| 80 std::string pattern = GetPattern(enabled); | 63 std::string pattern = GetPattern(enabled); |
| 81 if (file_util::WriteFile(FilePath(core_pattern_file_), | 64 if (file_util::WriteFile(FilePath(core_pattern_file_), |
| 82 pattern.c_str(), | 65 pattern.c_str(), |
| 83 pattern.length()) != | 66 pattern.length()) != |
| 84 static_cast<int>(pattern.length())) { | 67 static_cast<int>(pattern.length())) { |
| 85 logger_->LogError("Unable to write %s", core_pattern_file_.c_str()); | 68 logger_->LogError("Unable to write %s", core_pattern_file_.c_str()); |
| 86 return false; | 69 return false; |
| 87 } | 70 } |
| 88 return true; | 71 return true; |
| 89 } | 72 } |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 154 return false; | 137 return false; |
| 155 } | 138 } |
| 156 const char *number = ids[kind].c_str(); | 139 const char *number = ids[kind].c_str(); |
| 157 char *end_number = NULL; | 140 char *end_number = NULL; |
| 158 *id = strtol(number, &end_number, 10); | 141 *id = strtol(number, &end_number, 10); |
| 159 if (*end_number != '\0') | 142 if (*end_number != '\0') |
| 160 return false; | 143 return false; |
| 161 return true; | 144 return true; |
| 162 } | 145 } |
| 163 | 146 |
| 164 bool UserCollector::GetUserInfoFromName(const std::string &name, | |
| 165 uid_t *uid, | |
| 166 gid_t *gid) { | |
| 167 char storage[256]; | |
| 168 struct passwd passwd_storage; | |
| 169 struct passwd *passwd_result = NULL; | |
| 170 | |
| 171 if (getpwnam_r(name.c_str(), &passwd_storage, storage, sizeof(storage), | |
| 172 &passwd_result) != 0 || passwd_result == NULL) { | |
| 173 logger_->LogError("Cannot find user named %s", name.c_str()); | |
| 174 return false; | |
| 175 } | |
| 176 | |
| 177 *uid = passwd_result->pw_uid; | |
| 178 *gid = passwd_result->pw_gid; | |
| 179 return true; | |
| 180 } | |
| 181 | |
| 182 bool UserCollector::CopyOffProcFiles(pid_t pid, | 147 bool UserCollector::CopyOffProcFiles(pid_t pid, |
| 183 const FilePath &container_dir) { | 148 const FilePath &container_dir) { |
| 184 if (!file_util::CreateDirectory(container_dir)) { | 149 if (!file_util::CreateDirectory(container_dir)) { |
| 185 logger_->LogInfo("Could not create %s", container_dir.value().c_str()); | 150 logger_->LogInfo("Could not create %s", container_dir.value().c_str()); |
| 186 return false; | 151 return false; |
| 187 } | 152 } |
| 188 FilePath process_path = GetProcessPath(pid); | 153 FilePath process_path = GetProcessPath(pid); |
| 189 if (!file_util::PathExists(process_path)) { | 154 if (!file_util::PathExists(process_path)) { |
| 190 logger_->LogWarning("Path %s does not exist", | 155 logger_->LogWarning("Path %s does not exist", |
| 191 process_path.value().c_str()); | 156 process_path.value().c_str()); |
| 192 return false; | 157 return false; |
| 193 } | 158 } |
| 194 static const char *proc_files[] = { | 159 static const char *proc_files[] = { |
| 195 "auxv", | 160 "auxv", |
| 196 "cmdline", | 161 "cmdline", |
| 197 "environ", | 162 "environ", |
| 198 "maps", | 163 "maps", |
| 199 "status" | 164 "status" |
| 200 }; | 165 }; |
| 201 for (unsigned i = 0; i < arraysize(proc_files); ++i) { | 166 for (unsigned i = 0; i < arraysize(proc_files); ++i) { |
| 202 if (!file_util::CopyFile(process_path.Append(proc_files[i]), | 167 if (!file_util::CopyFile(process_path.Append(proc_files[i]), |
| 203 container_dir.Append(proc_files[i]))) { | 168 container_dir.Append(proc_files[i]))) { |
| 204 logger_->LogWarning("Could not copy %s file", proc_files[i]); | 169 logger_->LogWarning("Could not copy %s file", proc_files[i]); |
| 205 return false; | 170 return false; |
| 206 } | 171 } |
| 207 } | 172 } |
| 208 return true; | 173 return true; |
| 209 } | 174 } |
| 210 | 175 |
| 211 FilePath UserCollector::GetCrashDirectoryInfo( | |
| 212 uid_t process_euid, | |
| 213 uid_t default_user_id, | |
| 214 gid_t default_user_group, | |
| 215 mode_t *mode, | |
| 216 uid_t *directory_owner, | |
| 217 gid_t *directory_group) { | |
| 218 if (process_euid == default_user_id) { | |
| 219 *mode = kUserCrashPathMode; | |
| 220 *directory_owner = default_user_id; | |
| 221 *directory_group = default_user_group; | |
| 222 return FilePath(kUserCrashPath); | |
| 223 } else { | |
| 224 *mode = kSystemCrashPathMode; | |
| 225 *directory_owner = kRootOwner; | |
| 226 *directory_group = kRootGroup; | |
| 227 return FilePath(kSystemCrashPath); | |
| 228 } | |
| 229 } | |
| 230 | |
| 231 bool UserCollector::GetCreatedCrashDirectory(pid_t pid, | 176 bool UserCollector::GetCreatedCrashDirectory(pid_t pid, |
| 232 FilePath *crash_file_path) { | 177 FilePath *crash_file_path) { |
| 233 FilePath process_path = GetProcessPath(pid); | 178 FilePath process_path = GetProcessPath(pid); |
| 234 std::string status; | 179 std::string status; |
| 235 if (!file_util::ReadFileToString(process_path.Append("status"), | 180 if (!file_util::ReadFileToString(process_path.Append("status"), |
| 236 &status)) { | 181 &status)) { |
| 237 logger_->LogError("Could not read status file"); | 182 logger_->LogError("Could not read status file"); |
| 238 return false; | 183 return false; |
| 239 } | 184 } |
| 240 int process_euid; | 185 int process_euid; |
| 241 if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) { | 186 if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) { |
| 242 logger_->LogError("Could not find euid in status file"); | 187 logger_->LogError("Could not find euid in status file"); |
| 243 return false; | 188 return false; |
| 244 } | 189 } |
| 245 uid_t default_user_id; | 190 return GetCreatedCrashDirectoryByEuid(process_euid, |
| 246 gid_t default_user_group; | 191 crash_file_path); |
| 247 if (!GetUserInfoFromName(kDefaultUserName, | |
| 248 &default_user_id, | |
| 249 &default_user_group)) { | |
| 250 logger_->LogError("Could not find default user info"); | |
| 251 return false; | |
| 252 } | |
| 253 mode_t directory_mode; | |
| 254 uid_t directory_owner; | |
| 255 gid_t directory_group; | |
| 256 *crash_file_path = | |
| 257 GetCrashDirectoryInfo(process_euid, | |
| 258 default_user_id, | |
| 259 default_user_group, | |
| 260 &directory_mode, | |
| 261 &directory_owner, | |
| 262 &directory_group); | |
| 263 | |
| 264 | |
| 265 if (!file_util::PathExists(*crash_file_path)) { | |
| 266 // Create the spool directory with the appropriate mode (regardless of | |
| 267 // umask) and ownership. | |
| 268 mode_t old_mask = umask(0); | |
| 269 if (mkdir(crash_file_path->value().c_str(), directory_mode) < 0 || | |
| 270 chown(crash_file_path->value().c_str(), | |
| 271 directory_owner, | |
| 272 directory_group) < 0) { | |
| 273 logger_->LogError("Unable to create appropriate crash directory"); | |
| 274 return false; | |
| 275 } | |
| 276 umask(old_mask); | |
| 277 } | |
| 278 | |
| 279 if (!file_util::PathExists(*crash_file_path)) { | |
| 280 logger_->LogError("Unable to create crash directory %s", | |
| 281 crash_file_path->value().c_str()); | |
| 282 return false; | |
| 283 } | |
| 284 | |
| 285 | |
| 286 return true; | |
| 287 } | |
| 288 | |
| 289 std::string UserCollector::FormatDumpBasename(const std::string &exec_name, | |
| 290 time_t timestamp, | |
| 291 pid_t pid) { | |
| 292 struct tm tm; | |
| 293 localtime_r(×tamp, &tm); | |
| 294 return StringPrintf("%s.%04d%02d%02d.%02d%02d%02d.%d", | |
| 295 exec_name.c_str(), | |
| 296 tm.tm_year + 1900, | |
| 297 tm.tm_mon + 1, | |
| 298 tm.tm_mday, | |
| 299 tm.tm_hour, | |
| 300 tm.tm_min, | |
| 301 tm.tm_sec, | |
| 302 pid); | |
| 303 } | 192 } |
| 304 | 193 |
| 305 bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) { | 194 bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) { |
| 306 // Copy off all stdin to a core file. | 195 // Copy off all stdin to a core file. |
| 307 FilePath stdin_path("/dev/fd/0"); | 196 FilePath stdin_path("/dev/fd/0"); |
| 308 if (file_util::CopyFile(stdin_path, core_path)) { | 197 if (file_util::CopyFile(stdin_path, core_path)) { |
| 309 return true; | 198 return true; |
| 310 } | 199 } |
| 311 | 200 |
| 312 logger_->LogError("Could not write core file"); | 201 logger_->LogError("Could not write core file"); |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 353 const std::string &exec_name) { | 242 const std::string &exec_name) { |
| 354 FilePath container_dir("/tmp"); | 243 FilePath container_dir("/tmp"); |
| 355 container_dir = container_dir.Append( | 244 container_dir = container_dir.Append( |
| 356 StringPrintf("crash_reporter.%d", pid)); | 245 StringPrintf("crash_reporter.%d", pid)); |
| 357 | 246 |
| 358 if (!CopyOffProcFiles(pid, container_dir)) { | 247 if (!CopyOffProcFiles(pid, container_dir)) { |
| 359 file_util::Delete(container_dir, true); | 248 file_util::Delete(container_dir, true); |
| 360 return false; | 249 return false; |
| 361 } | 250 } |
| 362 | 251 |
| 363 FilePath spool_path; | 252 FilePath crash_path; |
| 364 if (!GetCreatedCrashDirectory(pid, &spool_path)) { | 253 if (!GetCreatedCrashDirectory(pid, &crash_path)) { |
| 365 file_util::Delete(container_dir, true); | 254 file_util::Delete(container_dir, true); |
| 366 return false; | 255 return false; |
| 367 } | 256 } |
| 368 std::string dump_basename = FormatDumpBasename(exec_name, time(NULL), pid); | 257 std::string dump_basename = FormatDumpBasename(exec_name, time(NULL), pid); |
| 369 FilePath core_path = spool_path.Append( | 258 FilePath core_path = crash_path.Append( |
| 370 StringPrintf("%s.core", dump_basename.c_str())); | 259 StringPrintf("%s.core", dump_basename.c_str())); |
| 371 | 260 |
| 372 if (!CopyStdinToCoreFile(core_path)) { | 261 if (!CopyStdinToCoreFile(core_path)) { |
| 373 file_util::Delete(container_dir, true); | 262 file_util::Delete(container_dir, true); |
| 374 return false; | 263 return false; |
| 375 } | 264 } |
| 376 | 265 |
| 377 FilePath minidump_path = spool_path.Append( | 266 FilePath minidump_path = crash_path.Append( |
| 378 StringPrintf("%s.dmp", dump_basename.c_str())); | 267 StringPrintf("%s.dmp", dump_basename.c_str())); |
| 379 | 268 |
| 380 bool conversion_result = true; | 269 bool conversion_result = true; |
| 381 if (!ConvertCoreToMinidump(core_path, | 270 if (!ConvertCoreToMinidump(core_path, |
| 382 container_dir, // procfs directory | 271 container_dir, // procfs directory |
| 383 minidump_path, | 272 minidump_path, |
| 384 container_dir)) { // temporary directory | 273 container_dir)) { // temporary directory |
| 385 // Note we leave the container directory for inspection. | 274 // Note we leave the container directory for inspection. |
| 386 conversion_result = false; | 275 conversion_result = false; |
| 387 } | 276 } |
| (...skipping 19 matching lines...) Expand all Loading... |
| 407 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) { | 296 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) { |
| 408 // If for some reason we don't have the base name, avoid completely | 297 // If for some reason we don't have the base name, avoid completely |
| 409 // failing by indicating an unknown name. | 298 // failing by indicating an unknown name. |
| 410 exec = "unknown"; | 299 exec = "unknown"; |
| 411 } | 300 } |
| 412 logger_->LogWarning("Received crash notification for %s[%d] sig %d", | 301 logger_->LogWarning("Received crash notification for %s[%d] sig %d", |
| 413 exec.c_str(), pid, signal); | 302 exec.c_str(), pid, signal); |
| 414 | 303 |
| 415 if (is_feedback_allowed_function_()) { | 304 if (is_feedback_allowed_function_()) { |
| 416 count_crash_function_(); | 305 count_crash_function_(); |
| 417 } | |
| 418 | 306 |
| 419 if (generate_diagnostics_) { | 307 if (generate_diagnostics_) { |
| 420 return GenerateDiagnostics(pid, exec); | 308 return GenerateDiagnostics(pid, exec); |
| 309 } |
| 421 } | 310 } |
| 422 return true; | 311 return true; |
| 423 } | 312 } |
| OLD | NEW |