blob: dda6333cc7aabd10b4dd379272216f1a6281ec0e [file] [log] [blame]
[email protected]9bc8cff2010-04-03 01:05:391// Copyright (c) 2010 The Chromium Authors. All rights reserved.
[email protected]75ae5422009-04-21 17:20:102// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]912c6452009-07-17 05:55:515#include "base/linux_util.h"
[email protected]75ae5422009-04-21 17:20:106
[email protected]85ebe8f2009-10-29 04:02:557#include <dirent.h>
8#include <errno.h>
[email protected]662183142010-07-16 19:28:179#include <fcntl.h>
[email protected]9e9b6e8e2009-12-02 08:45:0110#include <glib.h>
[email protected]75ae5422009-04-21 17:20:1011#include <stdlib.h>
[email protected]85ebe8f2009-10-29 04:02:5512#include <sys/stat.h>
[email protected]662183142010-07-16 19:28:1713#include <sys/stat.h>
14#include <sys/types.h>
[email protected]85ebe8f2009-10-29 04:02:5515#include <unistd.h>
[email protected]75ae5422009-04-21 17:20:1016
[email protected]912c6452009-07-17 05:55:5117#include <vector>
18
19#include "base/command_line.h"
[email protected]9bc8cff2010-04-03 01:05:3920#include "base/env_var.h"
[email protected]662183142010-07-16 19:28:1721#include "base/file_util.h"
[email protected]19467c02009-10-13 02:51:0722#include "base/lock.h"
[email protected]9e9b6e8e2009-12-02 08:45:0123#include "base/path_service.h"
[email protected]912c6452009-07-17 05:55:5124#include "base/process_util.h"
[email protected]19467c02009-10-13 02:51:0725#include "base/singleton.h"
[email protected]662183142010-07-16 19:28:1726#include "base/scoped_ptr.h"
[email protected]87fc1682009-07-22 00:22:4927#include "base/string_util.h"
28
29namespace {
30
[email protected]78f6f512009-10-21 19:04:1731// Not needed for OS_CHROMEOS.
32#if defined(OS_LINUX)
[email protected]19467c02009-10-13 02:51:0733enum LinuxDistroState {
34 STATE_DID_NOT_CHECK = 0,
35 STATE_CHECK_STARTED = 1,
36 STATE_CHECK_FINISHED = 2,
37};
38
39// Helper class for GetLinuxDistro().
40class LinuxDistroHelper {
41 public:
42 // Retrieves the Singleton.
43 static LinuxDistroHelper* Get() {
44 return Singleton<LinuxDistroHelper>::get();
45 }
46
47 // The simple state machine goes from:
48 // STATE_DID_NOT_CHECK -> STATE_CHECK_STARTED -> STATE_CHECK_FINISHED.
49 LinuxDistroHelper() : state_(STATE_DID_NOT_CHECK) {}
50 ~LinuxDistroHelper() {}
51
52 // Retrieve the current state, if we're in STATE_DID_NOT_CHECK,
53 // we automatically move to STATE_CHECK_STARTED so nobody else will
54 // do the check.
55 LinuxDistroState State() {
56 AutoLock scoped_lock(lock_);
57 if (STATE_DID_NOT_CHECK == state_) {
58 state_ = STATE_CHECK_STARTED;
59 return STATE_DID_NOT_CHECK;
60 }
61 return state_;
62 }
63
64 // Indicate the check finished, move to STATE_CHECK_FINISHED.
65 void CheckFinished() {
66 AutoLock scoped_lock(lock_);
67 DCHECK(state_ == STATE_CHECK_STARTED);
68 state_ = STATE_CHECK_FINISHED;
69 }
70
71 private:
72 Lock lock_;
73 LinuxDistroState state_;
74};
[email protected]78f6f512009-10-21 19:04:1775#endif // if defined(OS_LINUX)
[email protected]19467c02009-10-13 02:51:0776
[email protected]85ebe8f2009-10-29 04:02:5577// expected prefix of the target of the /proc/self/fd/%d link for a socket
78static const char kSocketLinkPrefix[] = "socket:[";
79
80// Parse a symlink in /proc/pid/fd/$x and return the inode number of the
81// socket.
82// inode_out: (output) set to the inode number on success
83// path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor)
84// log: if true, log messages about failure details
85bool ProcPathGetInode(ino_t* inode_out, const char* path, bool log = false) {
86 DCHECK(inode_out);
87 DCHECK(path);
88
89 char buf[256];
90 const ssize_t n = readlink(path, buf, sizeof(buf) - 1);
91 if (n == -1) {
92 if (log) {
93 LOG(WARNING) << "Failed to read the inode number for a socket from /proc"
94 "(" << errno << ")";
95 }
96 return false;
97 }
98 buf[n] = 0;
99
100 if (memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1)) {
101 if (log) {
102 LOG(WARNING) << "The descriptor passed from the crashing process wasn't a"
103 " UNIX domain socket.";
104 }
105 return false;
106 }
107
108 char *endptr;
109 const unsigned long long int inode_ul =
110 strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10);
111 if (*endptr != ']')
112 return false;
113
114 if (inode_ul == ULLONG_MAX) {
115 if (log) {
116 LOG(WARNING) << "Failed to parse a socket's inode number: the number was "
117 "too large. Please report this bug: " << buf;
118 }
119 return false;
120 }
121
122 *inode_out = inode_ul;
123 return true;
124}
125
[email protected]9bc8cff2010-04-03 01:05:39126} // namespace
[email protected]912c6452009-07-17 05:55:51127
[email protected]75ae5422009-04-21 17:20:10128namespace base {
129
[email protected]912c6452009-07-17 05:55:51130// We use this static string to hold the Linux distro info. If we
131// crash, the crash handler code will send this in the crash dump.
[email protected]78f6f512009-10-21 19:04:17132std::string linux_distro =
133#if defined(OS_CHROMEOS)
134 "CrOS";
135#else // if defined(OS_LINUX)
136 "Unknown";
137#endif
[email protected]912c6452009-07-17 05:55:51138
139std::string GetLinuxDistro() {
[email protected]78f6f512009-10-21 19:04:17140#if defined(OS_CHROMEOS)
141 return linux_distro;
[email protected]66700d42010-03-10 07:46:43142#elif defined(OS_LINUX)
[email protected]19467c02009-10-13 02:51:07143 LinuxDistroHelper* distro_state_singleton = LinuxDistroHelper::Get();
144 LinuxDistroState state = distro_state_singleton->State();
145 if (STATE_DID_NOT_CHECK == state) {
146 // We do this check only once per process. If it fails, there's
147 // little reason to believe it will work if we attempt to run
148 // lsb_release again.
[email protected]912c6452009-07-17 05:55:51149 std::vector<std::string> argv;
150 argv.push_back("lsb_release");
151 argv.push_back("-d");
152 std::string output;
153 base::GetAppOutput(CommandLine(argv), &output);
154 if (output.length() > 0) {
155 // lsb_release -d should return: Description:<tab>Distro Info
156 static const std::string field = "Description:\t";
[email protected]94aa5bb12009-10-21 22:09:33157 if (output.compare(0, field.length(), field) == 0) {
[email protected]912c6452009-07-17 05:55:51158 linux_distro = output.substr(field.length());
[email protected]94aa5bb12009-10-21 22:09:33159 TrimWhitespaceASCII(linux_distro, TRIM_ALL, &linux_distro);
160 }
[email protected]912c6452009-07-17 05:55:51161 }
[email protected]19467c02009-10-13 02:51:07162 distro_state_singleton->CheckFinished();
163 return 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.
167 return "Unknown";
168 } else {
169 // In STATE_CHECK_FINISHED, no more writing to |linux_distro|.
170 return linux_distro;
[email protected]912c6452009-07-17 05:55:51171 }
[email protected]66700d42010-03-10 07:46:43172#else
173 NOTIMPLEMENTED();
[email protected]78f6f512009-10-21 19:04:17174#endif
[email protected]912c6452009-07-17 05:55:51175}
176
[email protected]85ebe8f2009-10-29 04:02:55177bool FileDescriptorGetInode(ino_t* inode_out, int fd) {
178 DCHECK(inode_out);
179
180 struct stat buf;
181 if (fstat(fd, &buf) < 0)
182 return false;
183
184 if (!S_ISSOCK(buf.st_mode))
185 return false;
186
187 *inode_out = buf.st_ino;
188 return true;
189}
190
191bool FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode) {
192 DCHECK(pid_out);
193 bool already_found = false;
194
195 DIR* proc = opendir("/proc");
196 if (!proc) {
197 LOG(WARNING) << "Cannot open /proc";
198 return false;
199 }
200
201 std::vector<pid_t> pids;
202
203 struct dirent* dent;
204 while ((dent = readdir(proc))) {
205 char *endptr;
206 const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10);
207 if (pid_ul == ULONG_MAX || *endptr)
208 continue;
209 pids.push_back(pid_ul);
210 }
211 closedir(proc);
212
213 for (std::vector<pid_t>::const_iterator
214 i = pids.begin(); i != pids.end(); ++i) {
215 const pid_t current_pid = *i;
216 char buf[256];
217 snprintf(buf, sizeof(buf), "/proc/%d/fd", current_pid);
218 DIR* fd = opendir(buf);
219 if (!fd)
220 continue;
221
222 while ((dent = readdir(fd))) {
223 if (snprintf(buf, sizeof(buf), "/proc/%d/fd/%s", current_pid,
224 dent->d_name) >= static_cast<int>(sizeof(buf))) {
225 continue;
226 }
227
228 ino_t fd_inode;
229 if (ProcPathGetInode(&fd_inode, buf)) {
230 if (fd_inode == socket_inode) {
231 if (already_found) {
232 closedir(fd);
233 return false;
234 }
235
236 already_found = true;
237 *pid_out = current_pid;
238 break;
239 }
240 }
241 }
242
243 closedir(fd);
244 }
245
246 return already_found;
247}
248
[email protected]662183142010-07-16 19:28:17249pid_t FindThreadIDWithSyscall(pid_t pid, const std::string& expected_data) {
250 char buf[256];
251 snprintf(buf, sizeof(buf), "/proc/%d/task", pid);
252 DIR* task = opendir(buf);
253 if (!task) {
254 LOG(WARNING) << "Cannot open " << buf;
255 return -1;
256 }
257
258 std::vector<pid_t> tids;
259 struct dirent* dent;
260 while ((dent = readdir(task))) {
261 char *endptr;
262 const unsigned long int tid_ul = strtoul(dent->d_name, &endptr, 10);
263 if (tid_ul == ULONG_MAX || *endptr)
264 continue;
265 tids.push_back(tid_ul);
266 }
267 closedir(task);
268
269 scoped_array<char> syscall_data(new char[expected_data.length()]);
270 for (std::vector<pid_t>::const_iterator
271 i = tids.begin(); i != tids.end(); ++i) {
272 const pid_t current_tid = *i;
273 snprintf(buf, sizeof(buf), "/proc/%d/task/%d/syscall", pid, current_tid);
274 int fd = open(buf, O_RDONLY);
275 if (fd < 0)
276 continue;
277 bool read_ret =
278 file_util::ReadFromFD(fd, syscall_data.get(), expected_data.length());
279 close(fd);
280 if (!read_ret)
281 continue;
282
283 if (0 == strncmp(expected_data.c_str(), syscall_data.get(),
284 expected_data.length())) {
285 return current_tid;
286 }
287 }
288 return -1;
289}
290
[email protected]75ae5422009-04-21 17:20:10291} // namespace base