blob: 0194fc41ceefed66d5dd7f1b78f9b8b651ac4a49 [file] [log] [blame]
Ken Mixter03403162010-08-18 15:23:16 -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
5#include "crash-reporter/crash_collector.h"
6
Ken Mixter04ec10f2010-08-26 16:02:02 -07007#include <dirent.h>
Ken Mixter03403162010-08-18 15:23:16 -07008#include <pwd.h> // For struct passwd.
9#include <sys/types.h> // for mode_t.
10
11#include "base/file_util.h"
12#include "base/logging.h"
13#include "base/string_util.h"
14#include "crash-reporter/system_logging.h"
15
16static const char kDefaultUserName[] = "chronos";
17static const char kSystemCrashPath[] = "/var/spool/crash";
18static const char kUserCrashPath[] = "/home/chronos/user/crash";
19
20// Directory mode of the user crash spool directory.
21static const mode_t kUserCrashPathMode = 0755;
22
23// Directory mode of the system crash spool directory.
24static const mode_t kSystemCrashPathMode = 01755;
25
26static const uid_t kRootOwner = 0;
27static const uid_t kRootGroup = 0;
28
Ken Mixter04ec10f2010-08-26 16:02:02 -070029// Maximum of 8 crash reports per directory.
30const int CrashCollector::kMaxCrashDirectorySize = 8;
31
Ken Mixter03403162010-08-18 15:23:16 -070032CrashCollector::CrashCollector() : forced_crash_directory_(NULL) {
33}
34
35CrashCollector::~CrashCollector() {
36}
37
38void CrashCollector::Initialize(
39 CrashCollector::CountCrashFunction count_crash_function,
40 CrashCollector::IsFeedbackAllowedFunction is_feedback_allowed_function,
41 SystemLogging *logger) {
42 CHECK(count_crash_function != NULL);
43 CHECK(is_feedback_allowed_function != NULL);
44 CHECK(logger != NULL);
45
46 count_crash_function_ = count_crash_function;
47 is_feedback_allowed_function_ = is_feedback_allowed_function;
48 logger_ = logger;
49}
50
51std::string CrashCollector::FormatDumpBasename(const std::string &exec_name,
52 time_t timestamp,
53 pid_t pid) {
54 struct tm tm;
55 localtime_r(&timestamp, &tm);
56 return StringPrintf("%s.%04d%02d%02d.%02d%02d%02d.%d",
57 exec_name.c_str(),
58 tm.tm_year + 1900,
59 tm.tm_mon + 1,
60 tm.tm_mday,
61 tm.tm_hour,
62 tm.tm_min,
63 tm.tm_sec,
64 pid);
65}
66
67FilePath CrashCollector::GetCrashDirectoryInfo(
68 uid_t process_euid,
69 uid_t default_user_id,
70 gid_t default_user_group,
71 mode_t *mode,
72 uid_t *directory_owner,
73 gid_t *directory_group) {
74 if (process_euid == default_user_id) {
75 *mode = kUserCrashPathMode;
76 *directory_owner = default_user_id;
77 *directory_group = default_user_group;
78 return FilePath(kUserCrashPath);
79 } else {
80 *mode = kSystemCrashPathMode;
81 *directory_owner = kRootOwner;
82 *directory_group = kRootGroup;
83 return FilePath(kSystemCrashPath);
84 }
85}
86
87bool CrashCollector::GetUserInfoFromName(const std::string &name,
88 uid_t *uid,
89 gid_t *gid) {
90 char storage[256];
91 struct passwd passwd_storage;
92 struct passwd *passwd_result = NULL;
93
94 if (getpwnam_r(name.c_str(), &passwd_storage, storage, sizeof(storage),
95 &passwd_result) != 0 || passwd_result == NULL) {
96 logger_->LogError("Cannot find user named %s", name.c_str());
97 return false;
98 }
99
100 *uid = passwd_result->pw_uid;
101 *gid = passwd_result->pw_gid;
102 return true;
103}
104
105bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid,
106 FilePath *crash_directory) {
107 uid_t default_user_id;
108 gid_t default_user_group;
109
110 // For testing.
111 if (forced_crash_directory_ != NULL) {
112 *crash_directory = FilePath(forced_crash_directory_);
113 return true;
114 }
115
116 if (!GetUserInfoFromName(kDefaultUserName,
117 &default_user_id,
118 &default_user_group)) {
119 logger_->LogError("Could not find default user info");
120 return false;
121 }
122 mode_t directory_mode;
123 uid_t directory_owner;
124 gid_t directory_group;
125 *crash_directory =
126 GetCrashDirectoryInfo(euid,
127 default_user_id,
128 default_user_group,
129 &directory_mode,
130 &directory_owner,
131 &directory_group);
132
133 if (!file_util::PathExists(*crash_directory)) {
134 // Create the spool directory with the appropriate mode (regardless of
135 // umask) and ownership.
136 mode_t old_mask = umask(0);
137 if (mkdir(crash_directory->value().c_str(), directory_mode) < 0 ||
138 chown(crash_directory->value().c_str(),
139 directory_owner,
140 directory_group) < 0) {
141 logger_->LogError("Unable to create appropriate crash directory");
142 return false;
143 }
144 umask(old_mask);
145 }
146
147 if (!file_util::PathExists(*crash_directory)) {
148 logger_->LogError("Unable to create crash directory %s",
149 crash_directory->value().c_str());
150 return false;
151 }
152
Ken Mixter04ec10f2010-08-26 16:02:02 -0700153 if (!CheckHasCapacity(*crash_directory)) {
154 return false;
155 }
156
Ken Mixter03403162010-08-18 15:23:16 -0700157 return true;
158}
Ken Mixter04ec10f2010-08-26 16:02:02 -0700159
160// Return true if the given crash directory has not already reached
161// maximum capacity.
162bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) {
163 DIR* dir = opendir(crash_directory.value().c_str());
164 if (!dir) {
165 return false;
166 }
167 struct dirent ent_buf;
168 struct dirent* ent;
169 int count_non_core = 0;
170 int count_core = 0;
171 bool full = false;
172 while (readdir_r(dir, &ent_buf, &ent) == 0 && ent != NULL) {
173 if ((strcmp(ent->d_name, ".") == 0) ||
174 (strcmp(ent->d_name, "..") == 0))
175 continue;
176
177 if (strcmp(ent->d_name + strlen(ent->d_name) - 5, ".core") == 0) {
178 ++count_core;
179 } else {
180 ++count_non_core;
181 }
182
183 if (count_core >= kMaxCrashDirectorySize ||
184 count_non_core >= kMaxCrashDirectorySize) {
185 logger_->LogWarning(
186 "Crash directory %s already full with %d pending reports",
187 crash_directory.value().c_str(),
188 kMaxCrashDirectorySize);
189 full = true;
190 break;
191 }
192 }
193 closedir(dir);
194 return !full;
195}