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 |