1 // Copyright (c) 2010 The Chromium 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.
5 #include "base/linux_util.h"
14 #include <sys/types.h>
19 #include "base/command_line.h"
20 #include "base/file_util.h"
21 #include "base/lock.h"
22 #include "base/path_service.h"
23 #include "base/process_util.h"
24 #include "base/singleton.h"
25 #include "base/scoped_ptr.h"
26 #include "base/string_util.h"
30 // Not needed for OS_CHROMEOS.
32 enum LinuxDistroState
{
33 STATE_DID_NOT_CHECK
= 0,
34 STATE_CHECK_STARTED
= 1,
35 STATE_CHECK_FINISHED
= 2,
38 // Helper class for GetLinuxDistro().
39 class LinuxDistroHelper
{
41 // Retrieves the Singleton.
42 static LinuxDistroHelper
* Get() {
43 return Singleton
<LinuxDistroHelper
>::get();
46 // The simple state machine goes from:
47 // STATE_DID_NOT_CHECK -> STATE_CHECK_STARTED -> STATE_CHECK_FINISHED.
48 LinuxDistroHelper() : state_(STATE_DID_NOT_CHECK
) {}
49 ~LinuxDistroHelper() {}
51 // Retrieve the current state, if we're in STATE_DID_NOT_CHECK,
52 // we automatically move to STATE_CHECK_STARTED so nobody else will
54 LinuxDistroState
State() {
55 AutoLock
scoped_lock(lock_
);
56 if (STATE_DID_NOT_CHECK
== state_
) {
57 state_
= STATE_CHECK_STARTED
;
58 return STATE_DID_NOT_CHECK
;
63 // Indicate the check finished, move to STATE_CHECK_FINISHED.
64 void CheckFinished() {
65 AutoLock
scoped_lock(lock_
);
66 DCHECK(state_
== STATE_CHECK_STARTED
);
67 state_
= STATE_CHECK_FINISHED
;
72 LinuxDistroState state_
;
74 #endif // if defined(OS_LINUX)
76 // expected prefix of the target of the /proc/self/fd/%d link for a socket
77 static const char kSocketLinkPrefix
[] = "socket:[";
79 // Parse a symlink in /proc/pid/fd/$x and return the inode number of the
81 // inode_out: (output) set to the inode number on success
82 // path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor)
83 // log: if true, log messages about failure details
84 bool ProcPathGetInode(ino_t
* inode_out
, const char* path
, bool log
= false) {
89 const ssize_t n
= readlink(path
, buf
, sizeof(buf
) - 1);
92 LOG(WARNING
) << "Failed to read the inode number for a socket from /proc"
99 if (memcmp(kSocketLinkPrefix
, buf
, sizeof(kSocketLinkPrefix
) - 1)) {
101 LOG(WARNING
) << "The descriptor passed from the crashing process wasn't a"
102 " UNIX domain socket.";
108 const unsigned long long int inode_ul
=
109 strtoull(buf
+ sizeof(kSocketLinkPrefix
) - 1, &endptr
, 10);
113 if (inode_ul
== ULLONG_MAX
) {
115 LOG(WARNING
) << "Failed to parse a socket's inode number: the number was "
116 "too large. Please report this bug: " << buf
;
121 *inode_out
= inode_ul
;
129 // We use this static string to hold the Linux distro info. If we
130 // crash, the crash handler code will send this in the crash dump.
131 std::string linux_distro
=
132 #if defined(OS_CHROMEOS)
134 #else // if defined(OS_LINUX)
138 std::string
GetLinuxDistro() {
139 #if defined(OS_CHROMEOS)
141 #elif defined(OS_LINUX)
142 LinuxDistroHelper
* distro_state_singleton
= LinuxDistroHelper::Get();
143 LinuxDistroState state
= distro_state_singleton
->State();
144 if (STATE_DID_NOT_CHECK
== state
) {
145 // We do this check only once per process. If it fails, there's
146 // little reason to believe it will work if we attempt to run
147 // lsb_release again.
148 std::vector
<std::string
> argv
;
149 argv
.push_back("lsb_release");
150 argv
.push_back("-d");
152 base::GetAppOutput(CommandLine(argv
), &output
);
153 if (output
.length() > 0) {
154 // lsb_release -d should return: Description:<tab>Distro Info
155 static const std::string field
= "Description:\t";
156 if (output
.compare(0, field
.length(), field
) == 0) {
157 linux_distro
= output
.substr(field
.length());
158 TrimWhitespaceASCII(linux_distro
, TRIM_ALL
, &linux_distro
);
161 distro_state_singleton
->CheckFinished();
163 } else if (STATE_CHECK_STARTED
== state
) {
164 // If the distro check above is in progress in some other thread, we're
165 // not going to wait for the results.
168 // In STATE_CHECK_FINISHED, no more writing to |linux_distro|.
176 bool FileDescriptorGetInode(ino_t
* inode_out
, int fd
) {
180 if (fstat(fd
, &buf
) < 0)
183 if (!S_ISSOCK(buf
.st_mode
))
186 *inode_out
= buf
.st_ino
;
190 bool FindProcessHoldingSocket(pid_t
* pid_out
, ino_t socket_inode
) {
192 bool already_found
= false;
194 DIR* proc
= opendir("/proc");
196 LOG(WARNING
) << "Cannot open /proc";
200 std::vector
<pid_t
> pids
;
203 while ((dent
= readdir(proc
))) {
205 const unsigned long int pid_ul
= strtoul(dent
->d_name
, &endptr
, 10);
206 if (pid_ul
== ULONG_MAX
|| *endptr
)
208 pids
.push_back(pid_ul
);
212 for (std::vector
<pid_t
>::const_iterator
213 i
= pids
.begin(); i
!= pids
.end(); ++i
) {
214 const pid_t current_pid
= *i
;
216 snprintf(buf
, sizeof(buf
), "/proc/%d/fd", current_pid
);
217 DIR* fd
= opendir(buf
);
221 while ((dent
= readdir(fd
))) {
222 if (snprintf(buf
, sizeof(buf
), "/proc/%d/fd/%s", current_pid
,
223 dent
->d_name
) >= static_cast<int>(sizeof(buf
))) {
228 if (ProcPathGetInode(&fd_inode
, buf
)) {
229 if (fd_inode
== socket_inode
) {
235 already_found
= true;
236 *pid_out
= current_pid
;
245 return already_found
;
248 pid_t
FindThreadIDWithSyscall(pid_t pid
, const std::string
& expected_data
) {
250 snprintf(buf
, sizeof(buf
), "/proc/%d/task", pid
);
251 DIR* task
= opendir(buf
);
253 LOG(WARNING
) << "Cannot open " << buf
;
257 std::vector
<pid_t
> tids
;
259 while ((dent
= readdir(task
))) {
261 const unsigned long int tid_ul
= strtoul(dent
->d_name
, &endptr
, 10);
262 if (tid_ul
== ULONG_MAX
|| *endptr
)
264 tids
.push_back(tid_ul
);
268 scoped_array
<char> syscall_data(new char[expected_data
.length()]);
269 for (std::vector
<pid_t
>::const_iterator
270 i
= tids
.begin(); i
!= tids
.end(); ++i
) {
271 const pid_t current_tid
= *i
;
272 snprintf(buf
, sizeof(buf
), "/proc/%d/task/%d/syscall", pid
, current_tid
);
273 int fd
= open(buf
, O_RDONLY
);
277 file_util::ReadFromFD(fd
, syscall_data
.get(), expected_data
.length());
282 if (0 == strncmp(expected_data
.c_str(), syscall_data
.get(),
283 expected_data
.length())) {