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_EQ(STATE_CHECK_STARTED
, state_
);
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 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 DLOG(WARNING
) << "Failed to read the inode number for a socket from /proc"
98 if (memcmp(kSocketLinkPrefix
, buf
, sizeof(kSocketLinkPrefix
) - 1)) {
100 DLOG(WARNING
) << "The descriptor passed from the crashing process wasn't "
101 " a 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 DLOG(WARNING
) << "Failed to parse a socket's inode number: the number "
115 "was 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) && defined(USE_AURA)
136 #elif defined(OS_CHROMEOS)
138 #else // if defined(OS_LINUX)
142 std::string
GetLinuxDistro() {
143 #if defined(OS_CHROMEOS)
144 return g_linux_distro
;
145 #elif defined(OS_LINUX)
146 LinuxDistroHelper
* distro_state_singleton
= LinuxDistroHelper::GetInstance();
147 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.
152 std::vector
<std::string
> argv
;
153 argv
.push_back("lsb_release");
154 argv
.push_back("-d");
156 base::GetAppOutput(CommandLine(argv
), &output
);
157 if (output
.length() > 0) {
158 // lsb_release -d should return: Description:<tab>Distro Info
159 const char field
[] = "Description:\t";
160 if (output
.compare(0, strlen(field
), field
) == 0) {
161 SetLinuxDistro(output
.substr(strlen(field
)));
164 distro_state_singleton
->CheckFinished();
165 return g_linux_distro
;
166 } 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.
171 // In STATE_CHECK_FINISHED, no more writing to |linux_distro|.
172 return g_linux_distro
;
180 void 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
);
186 bool FileDescriptorGetInode(ino_t
* inode_out
, int fd
) {
190 if (fstat(fd
, &buf
) < 0)
193 if (!S_ISSOCK(buf
.st_mode
))
196 *inode_out
= buf
.st_ino
;
200 bool FindProcessHoldingSocket(pid_t
* pid_out
, ino_t socket_inode
) {
202 bool already_found
= false;
204 DIR* proc
= opendir("/proc");
206 DLOG(WARNING
) << "Cannot open /proc";
210 std::vector
<pid_t
> pids
;
213 while ((dent
= readdir(proc
))) {
215 const unsigned long int pid_ul
= strtoul(dent
->d_name
, &endptr
, 10);
216 if (pid_ul
== ULONG_MAX
|| *endptr
)
218 pids
.push_back(pid_ul
);
222 for (std::vector
<pid_t
>::const_iterator
223 i
= pids
.begin(); i
!= pids
.end(); ++i
) {
224 const pid_t current_pid
= *i
;
226 snprintf(buf
, sizeof(buf
), "/proc/%d/fd", current_pid
);
227 DIR* fd
= opendir(buf
);
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
))) {
238 if (ProcPathGetInode(&fd_inode
, buf
)) {
239 if (fd_inode
== socket_inode
) {
245 already_found
= true;
246 *pid_out
= current_pid
;
255 return already_found
;
258 pid_t
FindThreadIDWithSyscall(pid_t pid
, const std::string
& expected_data
,
259 bool* syscall_supported
) {
261 snprintf(buf
, sizeof(buf
), "/proc/%d/task", pid
);
263 if (syscall_supported
!= NULL
)
264 *syscall_supported
= false;
266 DIR* task
= opendir(buf
);
268 DLOG(WARNING
) << "Cannot open " << buf
;
272 std::vector
<pid_t
> tids
;
274 while ((dent
= readdir(task
))) {
276 const unsigned long int tid_ul
= strtoul(dent
->d_name
, &endptr
, 10);
277 if (tid_ul
== ULONG_MAX
|| *endptr
)
279 tids
.push_back(tid_ul
);
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
);
291 if (syscall_supported
!= NULL
)
292 *syscall_supported
= true;
294 file_util::ReadFromFD(fd
, syscall_data
.get(), expected_data
.length());
299 if (0 == strncmp(expected_data
.c_str(), syscall_data
.get(),
300 expected_data
.length())) {