1 // Copyright (c) 2012 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"
12 #include <sys/types.h>
17 #include "base/command_line.h"
18 #include "base/file_util.h"
19 #include "base/memory/scoped_ptr.h"
20 #include "base/memory/singleton.h"
21 #include "base/path_service.h"
22 #include "base/process/launch.h"
23 #include "base/strings/string_util.h"
24 #include "base/synchronization/lock.h"
28 // Not needed for OS_CHROMEOS.
30 enum LinuxDistroState
{
31 STATE_DID_NOT_CHECK
= 0,
32 STATE_CHECK_STARTED
= 1,
33 STATE_CHECK_FINISHED
= 2,
36 // Helper class for GetLinuxDistro().
37 class LinuxDistroHelper
{
39 // Retrieves the Singleton.
40 static LinuxDistroHelper
* GetInstance() {
41 return Singleton
<LinuxDistroHelper
>::get();
44 // The simple state machine goes from:
45 // STATE_DID_NOT_CHECK -> STATE_CHECK_STARTED -> STATE_CHECK_FINISHED.
46 LinuxDistroHelper() : state_(STATE_DID_NOT_CHECK
) {}
47 ~LinuxDistroHelper() {}
49 // Retrieve the current state, if we're in STATE_DID_NOT_CHECK,
50 // we automatically move to STATE_CHECK_STARTED so nobody else will
52 LinuxDistroState
State() {
53 base::AutoLock
scoped_lock(lock_
);
54 if (STATE_DID_NOT_CHECK
== state_
) {
55 state_
= STATE_CHECK_STARTED
;
56 return STATE_DID_NOT_CHECK
;
61 // Indicate the check finished, move to STATE_CHECK_FINISHED.
62 void CheckFinished() {
63 base::AutoLock
scoped_lock(lock_
);
64 DCHECK_EQ(STATE_CHECK_STARTED
, state_
);
65 state_
= STATE_CHECK_FINISHED
;
70 LinuxDistroState state_
;
72 #endif // if defined(OS_LINUX)
74 // expected prefix of the target of the /proc/self/fd/%d link for a socket
75 const char kSocketLinkPrefix
[] = "socket:[";
77 // Parse a symlink in /proc/pid/fd/$x and return the inode number of the
79 // inode_out: (output) set to the inode number on success
80 // path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor)
81 // log: if true, log messages about failure details
82 bool ProcPathGetInode(ino_t
* inode_out
, const char* path
, bool log
= false) {
87 const ssize_t n
= readlink(path
, buf
, sizeof(buf
) - 1);
90 DLOG(WARNING
) << "Failed to read the inode number for a socket from /proc"
97 if (memcmp(kSocketLinkPrefix
, buf
, sizeof(kSocketLinkPrefix
) - 1)) {
99 DLOG(WARNING
) << "The descriptor passed from the crashing process wasn't "
100 " a UNIX domain socket.";
106 const unsigned long long int inode_ul
=
107 strtoull(buf
+ sizeof(kSocketLinkPrefix
) - 1, &endptr
, 10);
111 if (inode_ul
== ULLONG_MAX
) {
113 DLOG(WARNING
) << "Failed to parse a socket's inode number: the number "
114 "was too large. Please report this bug: " << buf
;
119 *inode_out
= inode_ul
;
127 const char kFindInodeSwitch
[] = "--find-inode";
129 // Account for the terminating null character.
130 static const int kDistroSize
= 128 + 1;
132 // We use this static string to hold the Linux distro info. If we
133 // crash, the crash handler code will send this in the crash dump.
134 char g_linux_distro
[kDistroSize
] =
135 #if defined(OS_CHROMEOS)
137 #elif defined(OS_ANDROID)
139 #else // if defined(OS_LINUX)
143 std::string
GetLinuxDistro() {
144 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
145 return g_linux_distro
;
146 #elif defined(OS_LINUX)
147 LinuxDistroHelper
* distro_state_singleton
= LinuxDistroHelper::GetInstance();
148 LinuxDistroState state
= distro_state_singleton
->State();
149 if (STATE_CHECK_FINISHED
== state
)
150 return g_linux_distro
;
151 if (STATE_CHECK_STARTED
== state
)
152 return "Unknown"; // Don't wait for other thread to finish.
153 DCHECK_EQ(state
, STATE_DID_NOT_CHECK
);
154 // We do this check only once per process. If it fails, there's
155 // little reason to believe it will work if we attempt to run
156 // lsb_release again.
157 std::vector
<std::string
> argv
;
158 argv
.push_back("lsb_release");
159 argv
.push_back("-d");
161 base::GetAppOutput(CommandLine(argv
), &output
);
162 if (output
.length() > 0) {
163 // lsb_release -d should return: Description:<tab>Distro Info
164 const char field
[] = "Description:\t";
165 if (output
.compare(0, strlen(field
), field
) == 0) {
166 SetLinuxDistro(output
.substr(strlen(field
)));
169 distro_state_singleton
->CheckFinished();
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 DLOG(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
))) {
234 ino_t fd_inode
= static_cast<ino_t
>(-1);
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
,
256 bool* syscall_supported
) {
258 snprintf(buf
, sizeof(buf
), "/proc/%d/task", pid
);
260 if (syscall_supported
!= NULL
)
261 *syscall_supported
= false;
263 DIR* task
= opendir(buf
);
265 DLOG(WARNING
) << "Cannot open " << buf
;
269 std::vector
<pid_t
> tids
;
271 while ((dent
= readdir(task
))) {
273 const unsigned long int tid_ul
= strtoul(dent
->d_name
, &endptr
, 10);
274 if (tid_ul
== ULONG_MAX
|| *endptr
)
276 tids
.push_back(tid_ul
);
280 scoped_ptr
<char[]> syscall_data(new char[expected_data
.length()]);
281 for (std::vector
<pid_t
>::const_iterator
282 i
= tids
.begin(); i
!= tids
.end(); ++i
) {
283 const pid_t current_tid
= *i
;
284 snprintf(buf
, sizeof(buf
), "/proc/%d/task/%d/syscall", pid
, current_tid
);
285 int fd
= open(buf
, O_RDONLY
);
288 if (syscall_supported
!= NULL
)
289 *syscall_supported
= true;
291 file_util::ReadFromFD(fd
, syscall_data
.get(), expected_data
.length());
296 if (0 == strncmp(expected_data
.c_str(), syscall_data
.get(),
297 expected_data
.length())) {