1 // Copyright (c) 2011 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"
13 #include <sys/types.h>
18 #include "base/command_line.h"
19 #include "base/file_util.h"
20 #include "base/memory/scoped_ptr.h"
21 #include "base/memory/singleton.h"
22 #include "base/path_service.h"
23 #include "base/process_util.h"
24 #include "base/string_util.h"
25 #include "base/synchronization/lock.h"
29 // Not needed for OS_CHROMEOS.
31 enum LinuxDistroState
{
32 STATE_DID_NOT_CHECK
= 0,
33 STATE_CHECK_STARTED
= 1,
34 STATE_CHECK_FINISHED
= 2,
37 // Helper class for GetLinuxDistro().
38 class LinuxDistroHelper
{
40 // Retrieves the Singleton.
41 static LinuxDistroHelper
* GetInstance() {
42 return Singleton
<LinuxDistroHelper
>::get();
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() {}
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
53 LinuxDistroState
State() {
54 base::AutoLock
scoped_lock(lock_
);
55 if (STATE_DID_NOT_CHECK
== state_
) {
56 state_
= STATE_CHECK_STARTED
;
57 return STATE_DID_NOT_CHECK
;
62 // Indicate the check finished, move to STATE_CHECK_FINISHED.
63 void CheckFinished() {
64 base::AutoLock
scoped_lock(lock_
);
65 DCHECK(state_
== STATE_CHECK_STARTED
);
66 state_
= STATE_CHECK_FINISHED
;
71 LinuxDistroState state_
;
73 #endif // if defined(OS_LINUX)
75 // expected prefix of the target of the /proc/self/fd/%d link for a socket
76 static const char kSocketLinkPrefix
[] = "socket:[";
78 // Parse a symlink in /proc/pid/fd/$x and return the inode number of the
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
83 bool ProcPathGetInode(ino_t
* inode_out
, const char* path
, bool log
= false) {
88 const ssize_t n
= readlink(path
, buf
, sizeof(buf
) - 1);
91 LOG(WARNING
) << "Failed to read the inode number for a socket from /proc"
98 if (memcmp(kSocketLinkPrefix
, buf
, sizeof(kSocketLinkPrefix
) - 1)) {
100 LOG(WARNING
) << "The descriptor passed from the crashing process wasn't a"
101 " UNIX domain socket.";
107 const unsigned long long int inode_ul
=
108 strtoull(buf
+ sizeof(kSocketLinkPrefix
) - 1, &endptr
, 10);
112 if (inode_ul
== ULLONG_MAX
) {
114 LOG(WARNING
) << "Failed to parse a socket's inode number: the number was "
115 "too large. Please report this bug: " << buf
;
120 *inode_out
= inode_ul
;
128 // Account for the terminating null character.
129 static const int kDistroSize
= 128 + 1;
131 // 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.
133 char g_linux_distro
[kDistroSize
] =
134 #if defined(OS_CHROMEOS)
136 #else // if defined(OS_LINUX)
140 std::string
GetLinuxDistro() {
141 #if defined(OS_CHROMEOS)
142 return g_linux_distro
;
143 #elif defined(OS_LINUX)
144 LinuxDistroHelper
* distro_state_singleton
= LinuxDistroHelper::GetInstance();
145 LinuxDistroState state
= distro_state_singleton
->State();
146 if (STATE_DID_NOT_CHECK
== state
) {
147 // We do this check only once per process. If it fails, there's
148 // little reason to believe it will work if we attempt to run
149 // lsb_release again.
150 std::vector
<std::string
> argv
;
151 argv
.push_back("lsb_release");
152 argv
.push_back("-d");
154 base::GetAppOutput(CommandLine(argv
), &output
);
155 if (output
.length() > 0) {
156 // lsb_release -d should return: Description:<tab>Distro Info
157 static const std::string field
= "Description:\t";
158 if (output
.compare(0, field
.length(), field
) == 0) {
159 SetLinuxDistro(output
.substr(field
.length()));
162 distro_state_singleton
->CheckFinished();
163 return g_linux_distro
;
164 } else if (STATE_CHECK_STARTED
== state
) {
165 // If the distro check above is in progress in some other thread, we're
166 // not going to wait for the results.
169 // In STATE_CHECK_FINISHED, no more writing to |linux_distro|.
170 return g_linux_distro
;
177 void SetLinuxDistro(const std::string
& distro
) {
178 std::string trimmed_distro
;
179 TrimWhitespaceASCII(distro
, TRIM_ALL
, &trimmed_distro
);
180 base::strlcpy(g_linux_distro
, trimmed_distro
.c_str(), kDistroSize
);
183 bool FileDescriptorGetInode(ino_t
* inode_out
, int fd
) {
187 if (fstat(fd
, &buf
) < 0)
190 if (!S_ISSOCK(buf
.st_mode
))
193 *inode_out
= buf
.st_ino
;
197 bool FindProcessHoldingSocket(pid_t
* pid_out
, ino_t socket_inode
) {
199 bool already_found
= false;
201 DIR* proc
= opendir("/proc");
203 LOG(WARNING
) << "Cannot open /proc";
207 std::vector
<pid_t
> pids
;
210 while ((dent
= readdir(proc
))) {
212 const unsigned long int pid_ul
= strtoul(dent
->d_name
, &endptr
, 10);
213 if (pid_ul
== ULONG_MAX
|| *endptr
)
215 pids
.push_back(pid_ul
);
219 for (std::vector
<pid_t
>::const_iterator
220 i
= pids
.begin(); i
!= pids
.end(); ++i
) {
221 const pid_t current_pid
= *i
;
223 snprintf(buf
, sizeof(buf
), "/proc/%d/fd", current_pid
);
224 DIR* fd
= opendir(buf
);
228 while ((dent
= readdir(fd
))) {
229 if (snprintf(buf
, sizeof(buf
), "/proc/%d/fd/%s", current_pid
,
230 dent
->d_name
) >= static_cast<int>(sizeof(buf
))) {
235 if (ProcPathGetInode(&fd_inode
, buf
)) {
236 if (fd_inode
== socket_inode
) {
242 already_found
= true;
243 *pid_out
= current_pid
;
252 return already_found
;
255 pid_t
FindThreadIDWithSyscall(pid_t pid
, const std::string
& expected_data
) {
257 snprintf(buf
, sizeof(buf
), "/proc/%d/task", pid
);
258 DIR* task
= opendir(buf
);
260 LOG(WARNING
) << "Cannot open " << buf
;
264 std::vector
<pid_t
> tids
;
266 while ((dent
= readdir(task
))) {
268 const unsigned long int tid_ul
= strtoul(dent
->d_name
, &endptr
, 10);
269 if (tid_ul
== ULONG_MAX
|| *endptr
)
271 tids
.push_back(tid_ul
);
275 scoped_array
<char> syscall_data(new char[expected_data
.length()]);
276 for (std::vector
<pid_t
>::const_iterator
277 i
= tids
.begin(); i
!= tids
.end(); ++i
) {
278 const pid_t current_tid
= *i
;
279 snprintf(buf
, sizeof(buf
), "/proc/%d/task/%d/syscall", pid
, current_tid
);
280 int fd
= open(buf
, O_RDONLY
);
284 file_util::ReadFromFD(fd
, syscall_data
.get(), expected_data
.length());
289 if (0 == strncmp(expected_data
.c_str(), syscall_data
.get(),
290 expected_data
.length())) {