blob: 0a9ef56d88eb06d0b3876e2c6fbac2e3057eee82 [file] [log] [blame]
levin@chromium.org5c528682011-03-28 10:54:15 +09001// Copyright (c) 2011 The Chromium Authors. All rights reserved.
sgk@google.come1f4a242009-04-22 02:20:10 +09002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
thestig@chromium.orga5a2b6e2009-07-17 14:55:51 +09005#include "base/linux_util.h"
sgk@google.come1f4a242009-04-22 02:20:10 +09006
thestig@chromium.org2feb1c82009-10-29 13:02:55 +09007#include <dirent.h>
8#include <errno.h>
thestig@chromium.org44836d42010-07-17 04:28:17 +09009#include <fcntl.h>
thestig@chromium.orgeea83062009-12-02 17:45:01 +090010#include <glib.h>
sgk@google.come1f4a242009-04-22 02:20:10 +090011#include <stdlib.h>
thestig@chromium.org2feb1c82009-10-29 13:02:55 +090012#include <sys/stat.h>
thestig@chromium.org44836d42010-07-17 04:28:17 +090013#include <sys/types.h>
thestig@chromium.org2feb1c82009-10-29 13:02:55 +090014#include <unistd.h>
sgk@google.come1f4a242009-04-22 02:20:10 +090015
thestig@chromium.orga5a2b6e2009-07-17 14:55:51 +090016#include <vector>
17
18#include "base/command_line.h"
thestig@chromium.org44836d42010-07-17 04:28:17 +090019#include "base/file_util.h"
levin@chromium.org5c528682011-03-28 10:54:15 +090020#include "base/memory/scoped_ptr.h"
21#include "base/memory/singleton.h"
thestig@chromium.orgeea83062009-12-02 17:45:01 +090022#include "base/path_service.h"
thestig@chromium.orga5a2b6e2009-07-17 14:55:51 +090023#include "base/process_util.h"
mattm@chromium.org625865e2009-07-22 09:22:49 +090024#include "base/string_util.h"
brettw@chromium.orgabe477a2011-01-21 13:55:52 +090025#include "base/synchronization/lock.h"
mattm@chromium.org625865e2009-07-22 09:22:49 +090026
27namespace {
28
thestig@chromium.org9d0e95c2009-10-22 04:04:17 +090029// Not needed for OS_CHROMEOS.
30#if defined(OS_LINUX)
thestig@chromium.org741196d2009-10-13 11:51:07 +090031enum LinuxDistroState {
32 STATE_DID_NOT_CHECK = 0,
33 STATE_CHECK_STARTED = 1,
34 STATE_CHECK_FINISHED = 2,
35};
36
37// Helper class for GetLinuxDistro().
38class LinuxDistroHelper {
39 public:
40 // Retrieves the Singleton.
satish@chromium.orgce3f4872010-12-14 01:52:35 +090041 static LinuxDistroHelper* GetInstance() {
thestig@chromium.org741196d2009-10-13 11:51:07 +090042 return Singleton<LinuxDistroHelper>::get();
43 }
44
45 // The simple state machine goes from:
46 // STATE_DID_NOT_CHECK -> STATE_CHECK_STARTED -> STATE_CHECK_FINISHED.
47 LinuxDistroHelper() : state_(STATE_DID_NOT_CHECK) {}
48 ~LinuxDistroHelper() {}
49
50 // Retrieve the current state, if we're in STATE_DID_NOT_CHECK,
51 // we automatically move to STATE_CHECK_STARTED so nobody else will
52 // do the check.
53 LinuxDistroState State() {
brettw@chromium.orgabe477a2011-01-21 13:55:52 +090054 base::AutoLock scoped_lock(lock_);
thestig@chromium.org741196d2009-10-13 11:51:07 +090055 if (STATE_DID_NOT_CHECK == state_) {
56 state_ = STATE_CHECK_STARTED;
57 return STATE_DID_NOT_CHECK;
58 }
59 return state_;
60 }
61
62 // Indicate the check finished, move to STATE_CHECK_FINISHED.
63 void CheckFinished() {
brettw@chromium.orgabe477a2011-01-21 13:55:52 +090064 base::AutoLock scoped_lock(lock_);
david.mike.futcher@gmail.com9eb2aa52011-04-19 05:07:08 +090065 DCHECK_EQ(STATE_CHECK_STARTED, state_);
thestig@chromium.org741196d2009-10-13 11:51:07 +090066 state_ = STATE_CHECK_FINISHED;
67 }
68
69 private:
brettw@chromium.orgabe477a2011-01-21 13:55:52 +090070 base::Lock lock_;
thestig@chromium.org741196d2009-10-13 11:51:07 +090071 LinuxDistroState state_;
72};
thestig@chromium.org9d0e95c2009-10-22 04:04:17 +090073#endif // if defined(OS_LINUX)
thestig@chromium.org741196d2009-10-13 11:51:07 +090074
thestig@chromium.org2feb1c82009-10-29 13:02:55 +090075// expected prefix of the target of the /proc/self/fd/%d link for a socket
jhawkins@chromium.org88fbaf62012-01-28 09:34:40 +090076const char kSocketLinkPrefix[] = "socket:[";
thestig@chromium.org2feb1c82009-10-29 13:02:55 +090077
78// Parse a symlink in /proc/pid/fd/$x and return the inode number of the
79// socket.
80// inode_out: (output) set to the inode number on success
81// path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor)
82// log: if true, log messages about failure details
83bool ProcPathGetInode(ino_t* inode_out, const char* path, bool log = false) {
84 DCHECK(inode_out);
85 DCHECK(path);
86
87 char buf[256];
88 const ssize_t n = readlink(path, buf, sizeof(buf) - 1);
89 if (n == -1) {
90 if (log) {
brettw@chromium.org5faed3c2011-10-27 06:48:00 +090091 DLOG(WARNING) << "Failed to read the inode number for a socket from /proc"
thestig@chromium.org2feb1c82009-10-29 13:02:55 +090092 "(" << errno << ")";
93 }
94 return false;
95 }
96 buf[n] = 0;
97
98 if (memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1)) {
99 if (log) {
brettw@chromium.org5faed3c2011-10-27 06:48:00 +0900100 DLOG(WARNING) << "The descriptor passed from the crashing process wasn't "
101 " a UNIX domain socket.";
thestig@chromium.org2feb1c82009-10-29 13:02:55 +0900102 }
103 return false;
104 }
105
106 char *endptr;
107 const unsigned long long int inode_ul =
108 strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10);
109 if (*endptr != ']')
110 return false;
111
112 if (inode_ul == ULLONG_MAX) {
113 if (log) {
brettw@chromium.org5faed3c2011-10-27 06:48:00 +0900114 DLOG(WARNING) << "Failed to parse a socket's inode number: the number "
115 "was too large. Please report this bug: " << buf;
thestig@chromium.org2feb1c82009-10-29 13:02:55 +0900116 }
117 return false;
118 }
119
120 *inode_out = inode_ul;
121 return true;
122}
123
thestig@chromium.orgb6ba9432010-04-03 10:05:39 +0900124} // namespace
thestig@chromium.orga5a2b6e2009-07-17 14:55:51 +0900125
sgk@google.come1f4a242009-04-22 02:20:10 +0900126namespace base {
127
mnissler@chromium.org8584b6d2010-08-26 17:55:22 +0900128// Account for the terminating null character.
129static const int kDistroSize = 128 + 1;
130
thestig@chromium.orga5a2b6e2009-07-17 14:55:51 +0900131// We use this static string to hold the Linux distro info. If we
132// crash, the crash handler code will send this in the crash dump.
mnissler@chromium.org8584b6d2010-08-26 17:55:22 +0900133char g_linux_distro[kDistroSize] =
saintlou@chromium.org6716159d2011-11-23 11:07:45 +0900134#if defined(OS_CHROMEOS) && defined(USE_AURA)
135 "CrOS Aura";
rbyers@chromium.org9f235682011-11-11 01:04:02 +0900136#elif defined(OS_CHROMEOS)
thestig@chromium.org9d0e95c2009-10-22 04:04:17 +0900137 "CrOS";
138#else // if defined(OS_LINUX)
139 "Unknown";
140#endif
thestig@chromium.orga5a2b6e2009-07-17 14:55:51 +0900141
142std::string GetLinuxDistro() {
thestig@chromium.org9d0e95c2009-10-22 04:04:17 +0900143#if defined(OS_CHROMEOS)
mnissler@chromium.org8584b6d2010-08-26 17:55:22 +0900144 return g_linux_distro;
pvalchev@google.com1d919db2010-03-10 16:46:43 +0900145#elif defined(OS_LINUX)
satish@chromium.orgce3f4872010-12-14 01:52:35 +0900146 LinuxDistroHelper* distro_state_singleton = LinuxDistroHelper::GetInstance();
thestig@chromium.org741196d2009-10-13 11:51:07 +0900147 LinuxDistroState state = distro_state_singleton->State();
148 if (STATE_DID_NOT_CHECK == state) {
149 // We do this check only once per process. If it fails, there's
150 // little reason to believe it will work if we attempt to run
151 // lsb_release again.
thestig@chromium.orga5a2b6e2009-07-17 14:55:51 +0900152 std::vector<std::string> argv;
153 argv.push_back("lsb_release");
154 argv.push_back("-d");
155 std::string output;
156 base::GetAppOutput(CommandLine(argv), &output);
157 if (output.length() > 0) {
158 // lsb_release -d should return: Description:<tab>Distro Info
thakis@chromium.org96473bc2011-11-10 06:55:08 +0900159 const char field[] = "Description:\t";
160 if (output.compare(0, strlen(field), field) == 0) {
161 SetLinuxDistro(output.substr(strlen(field)));
thestig@chromium.orgb5299f12009-10-22 07:09:33 +0900162 }
thestig@chromium.orga5a2b6e2009-07-17 14:55:51 +0900163 }
thestig@chromium.org741196d2009-10-13 11:51:07 +0900164 distro_state_singleton->CheckFinished();
mnissler@chromium.org8584b6d2010-08-26 17:55:22 +0900165 return g_linux_distro;
thestig@chromium.org741196d2009-10-13 11:51:07 +0900166 } else if (STATE_CHECK_STARTED == state) {
167 // If the distro check above is in progress in some other thread, we're
168 // not going to wait for the results.
169 return "Unknown";
170 } else {
171 // In STATE_CHECK_FINISHED, no more writing to |linux_distro|.
mnissler@chromium.org8584b6d2010-08-26 17:55:22 +0900172 return g_linux_distro;
thestig@chromium.orga5a2b6e2009-07-17 14:55:51 +0900173 }
pvalchev@google.com1d919db2010-03-10 16:46:43 +0900174#else
175 NOTIMPLEMENTED();
robert.nagy@gmail.comea54e462011-10-25 07:05:27 +0900176 return "Unknown";
thestig@chromium.org9d0e95c2009-10-22 04:04:17 +0900177#endif
thestig@chromium.orga5a2b6e2009-07-17 14:55:51 +0900178}
179
mnissler@chromium.org8584b6d2010-08-26 17:55:22 +0900180void SetLinuxDistro(const std::string& distro) {
181 std::string trimmed_distro;
182 TrimWhitespaceASCII(distro, TRIM_ALL, &trimmed_distro);
183 base::strlcpy(g_linux_distro, trimmed_distro.c_str(), kDistroSize);
184}
185
thestig@chromium.org2feb1c82009-10-29 13:02:55 +0900186bool FileDescriptorGetInode(ino_t* inode_out, int fd) {
187 DCHECK(inode_out);
188
189 struct stat buf;
190 if (fstat(fd, &buf) < 0)
191 return false;
192
193 if (!S_ISSOCK(buf.st_mode))
194 return false;
195
196 *inode_out = buf.st_ino;
197 return true;
198}
199
200bool FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode) {
201 DCHECK(pid_out);
202 bool already_found = false;
203
204 DIR* proc = opendir("/proc");
205 if (!proc) {
brettw@chromium.org5faed3c2011-10-27 06:48:00 +0900206 DLOG(WARNING) << "Cannot open /proc";
thestig@chromium.org2feb1c82009-10-29 13:02:55 +0900207 return false;
208 }
209
210 std::vector<pid_t> pids;
211
212 struct dirent* dent;
213 while ((dent = readdir(proc))) {
214 char *endptr;
215 const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10);
216 if (pid_ul == ULONG_MAX || *endptr)
217 continue;
218 pids.push_back(pid_ul);
219 }
220 closedir(proc);
221
222 for (std::vector<pid_t>::const_iterator
223 i = pids.begin(); i != pids.end(); ++i) {
224 const pid_t current_pid = *i;
225 char buf[256];
226 snprintf(buf, sizeof(buf), "/proc/%d/fd", current_pid);
227 DIR* fd = opendir(buf);
228 if (!fd)
229 continue;
230
231 while ((dent = readdir(fd))) {
232 if (snprintf(buf, sizeof(buf), "/proc/%d/fd/%s", current_pid,
233 dent->d_name) >= static_cast<int>(sizeof(buf))) {
234 continue;
235 }
236
237 ino_t fd_inode;
238 if (ProcPathGetInode(&fd_inode, buf)) {
239 if (fd_inode == socket_inode) {
240 if (already_found) {
241 closedir(fd);
242 return false;
243 }
244
245 already_found = true;
246 *pid_out = current_pid;
247 break;
248 }
249 }
250 }
251
252 closedir(fd);
253 }
254
255 return already_found;
256}
257
kmixter@chromium.orge406ed72011-06-21 13:21:06 +0900258pid_t FindThreadIDWithSyscall(pid_t pid, const std::string& expected_data,
259 bool* syscall_supported) {
thestig@chromium.org44836d42010-07-17 04:28:17 +0900260 char buf[256];
261 snprintf(buf, sizeof(buf), "/proc/%d/task", pid);
kmixter@chromium.orge406ed72011-06-21 13:21:06 +0900262
263 if (syscall_supported != NULL)
264 *syscall_supported = false;
265
thestig@chromium.org44836d42010-07-17 04:28:17 +0900266 DIR* task = opendir(buf);
267 if (!task) {
brettw@chromium.org5faed3c2011-10-27 06:48:00 +0900268 DLOG(WARNING) << "Cannot open " << buf;
thestig@chromium.org44836d42010-07-17 04:28:17 +0900269 return -1;
270 }
271
272 std::vector<pid_t> tids;
273 struct dirent* dent;
274 while ((dent = readdir(task))) {
275 char *endptr;
276 const unsigned long int tid_ul = strtoul(dent->d_name, &endptr, 10);
277 if (tid_ul == ULONG_MAX || *endptr)
278 continue;
279 tids.push_back(tid_ul);
280 }
281 closedir(task);
282
283 scoped_array<char> syscall_data(new char[expected_data.length()]);
284 for (std::vector<pid_t>::const_iterator
285 i = tids.begin(); i != tids.end(); ++i) {
286 const pid_t current_tid = *i;
287 snprintf(buf, sizeof(buf), "/proc/%d/task/%d/syscall", pid, current_tid);
288 int fd = open(buf, O_RDONLY);
289 if (fd < 0)
290 continue;
kmixter@chromium.orge406ed72011-06-21 13:21:06 +0900291 if (syscall_supported != NULL)
292 *syscall_supported = true;
thestig@chromium.org44836d42010-07-17 04:28:17 +0900293 bool read_ret =
294 file_util::ReadFromFD(fd, syscall_data.get(), expected_data.length());
295 close(fd);
296 if (!read_ret)
297 continue;
298
299 if (0 == strncmp(expected_data.c_str(), syscall_data.get(),
300 expected_data.length())) {
301 return current_tid;
302 }
303 }
304 return -1;
305}
306
sgk@google.come1f4a242009-04-22 02:20:10 +0900307} // namespace base