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. 4 5 #include "base/linux_util.h" 6 7 #include <dirent.h> 8 #include <errno.h> 9 #include <fcntl.h> 10 #include <glib.h> 11 #include <stdlib.h> 12 #include <sys/stat.h> 13 #include <sys/types.h> 14 #include <unistd.h> 15 16 #include <vector> 17 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" 26 27 namespace { 28 29 // Not needed for OS_CHROMEOS. 30 #if defined(OS_LINUX) 31 enum LinuxDistroState { 32 STATE_DID_NOT_CHECK = 0, 33 STATE_CHECK_STARTED = 1, 34 STATE_CHECK_FINISHED = 2, 35 }; 36 37 // Helper class for GetLinuxDistro(). 38 class LinuxDistroHelper { 39 public: 40 // Retrieves the Singleton. 41 static LinuxDistroHelper* GetInstance() { 42 return Singleton<LinuxDistroHelper>::get(); 43 } 44 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() {} 49 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 52 // do the check. 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; 58 } 59 return state_; 60 } 61 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; 67 } 68 69 private: 70 base::Lock lock_; 71 LinuxDistroState state_; 72 }; 73 #endif // if defined(OS_LINUX) 74 75 // expected prefix of the target of the /proc/self/fd/%d link for a socket 76 static const char kSocketLinkPrefix[] = "socket:["; 77 78 // Parse a symlink in /proc/pid/fd/$x and return the inode number of the 79 // socket. 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) { 84 DCHECK(inode_out); 85 DCHECK(path); 86 87 char buf[256]; 88 const ssize_t n = readlink(path, buf, sizeof(buf) - 1); 89 if (n == -1) { 90 if (log) { 91 LOG(WARNING) << "Failed to read the inode number for a socket from /proc" 92 "(" << errno << ")"; 93 } 94 return false; 95 } 96 buf[n] = 0; 97 98 if (memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1)) { 99 if (log) { 100 LOG(WARNING) << "The descriptor passed from the crashing process wasn't a" 101 " UNIX domain socket."; 102 } 103 return false; 104 } 105 106 char *endptr; 107 const unsigned long long int inode_ul = 108 strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10); 109 if (*endptr != ']') 110 return false; 111 112 if (inode_ul == ULLONG_MAX) { 113 if (log) { 114 LOG(WARNING) << "Failed to parse a socket's inode number: the number was " 115 "too large. Please report this bug: " << buf; 116 } 117 return false; 118 } 119 120 *inode_out = inode_ul; 121 return true; 122 } 123 124 } // namespace 125 126 namespace base { 127 128 // Account for the terminating null character. 129 static const int kDistroSize = 128 + 1; 130 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) 135 "CrOS"; 136 #else // if defined(OS_LINUX) 137 "Unknown"; 138 #endif 139 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"); 153 std::string output; 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())); 160 } 161 } 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. 167 return "Unknown"; 168 } else { 169 // In STATE_CHECK_FINISHED, no more writing to |linux_distro|. 170 return g_linux_distro; 171 } 172 #else 173 NOTIMPLEMENTED(); 174 #endif 175 } 176 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); 181 } 182 183 bool FileDescriptorGetInode(ino_t* inode_out, int fd) { 184 DCHECK(inode_out); 185 186 struct stat buf; 187 if (fstat(fd, &buf) < 0) 188 return false; 189 190 if (!S_ISSOCK(buf.st_mode)) 191 return false; 192 193 *inode_out = buf.st_ino; 194 return true; 195 } 196 197 bool FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode) { 198 DCHECK(pid_out); 199 bool already_found = false; 200 201 DIR* proc = opendir("/proc"); 202 if (!proc) { 203 LOG(WARNING) << "Cannot open /proc"; 204 return false; 205 } 206 207 std::vector<pid_t> pids; 208 209 struct dirent* dent; 210 while ((dent = readdir(proc))) { 211 char *endptr; 212 const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10); 213 if (pid_ul == ULONG_MAX || *endptr) 214 continue; 215 pids.push_back(pid_ul); 216 } 217 closedir(proc); 218 219 for (std::vector<pid_t>::const_iterator 220 i = pids.begin(); i != pids.end(); ++i) { 221 const pid_t current_pid = *i; 222 char buf[256]; 223 snprintf(buf, sizeof(buf), "/proc/%d/fd", current_pid); 224 DIR* fd = opendir(buf); 225 if (!fd) 226 continue; 227 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))) { 231 continue; 232 } 233 234 ino_t fd_inode; 235 if (ProcPathGetInode(&fd_inode, buf)) { 236 if (fd_inode == socket_inode) { 237 if (already_found) { 238 closedir(fd); 239 return false; 240 } 241 242 already_found = true; 243 *pid_out = current_pid; 244 break; 245 } 246 } 247 } 248 249 closedir(fd); 250 } 251 252 return already_found; 253 } 254 255 pid_t FindThreadIDWithSyscall(pid_t pid, const std::string& expected_data) { 256 char buf[256]; 257 snprintf(buf, sizeof(buf), "/proc/%d/task", pid); 258 DIR* task = opendir(buf); 259 if (!task) { 260 LOG(WARNING) << "Cannot open " << buf; 261 return -1; 262 } 263 264 std::vector<pid_t> tids; 265 struct dirent* dent; 266 while ((dent = readdir(task))) { 267 char *endptr; 268 const unsigned long int tid_ul = strtoul(dent->d_name, &endptr, 10); 269 if (tid_ul == ULONG_MAX || *endptr) 270 continue; 271 tids.push_back(tid_ul); 272 } 273 closedir(task); 274 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); 281 if (fd < 0) 282 continue; 283 bool read_ret = 284 file_util::ReadFromFD(fd, syscall_data.get(), expected_data.length()); 285 close(fd); 286 if (!read_ret) 287 continue; 288 289 if (0 == strncmp(expected_data.c_str(), syscall_data.get(), 290 expected_data.length())) { 291 return current_tid; 292 } 293 } 294 return -1; 295 } 296 297 } // namespace base 298