Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(24)

Side by Side Diff: user_collector.cc

Issue 3179006: Collect and send kernel crash diagnostics (Closed) Base URL: ssh://git@chromiumos-git//crash-reporter.git
Patch Set: Respond to reviews Created 10 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « user_collector.h ('k') | user_collector_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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(&timestamp, &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
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
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 }
OLDNEW
« no previous file with comments | « user_collector.h ('k') | user_collector_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698