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. 4 5 #include "base/linux_util.h" 6 7 #include <dirent.h> 8 #include <errno.h> 9 #include <fcntl.h> 10 #include <stdlib.h> 11 #include <sys/stat.h> 12 #include <sys/types.h> 13 #include <unistd.h> 14 15 #include <vector> 16 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" 25 26 namespace { 27 28 // Not needed for OS_CHROMEOS. 29 #if defined(OS_LINUX) 30 enum LinuxDistroState { 31 STATE_DID_NOT_CHECK = 0, 32 STATE_CHECK_STARTED = 1, 33 STATE_CHECK_FINISHED = 2, 34 }; 35 36 // Helper class for GetLinuxDistro(). 37 class LinuxDistroHelper { 38 public: 39 // Retrieves the Singleton. 40 static LinuxDistroHelper* GetInstance() { 41 return Singleton<LinuxDistroHelper>::get(); 42 } 43 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() {} 48 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 51 // do the check. 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; 57 } 58 return state_; 59 } 60 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; 66 } 67 68 private: 69 base::Lock lock_; 70 LinuxDistroState state_; 71 }; 72 #endif // if defined(OS_LINUX) 73 74 // expected prefix of the target of the /proc/self/fd/%d link for a socket 75 const char kSocketLinkPrefix[] = "socket:["; 76 77 // Parse a symlink in /proc/pid/fd/$x and return the inode number of the 78 // socket. 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) { 83 DCHECK(inode_out); 84 DCHECK(path); 85 86 char buf[256]; 87 const ssize_t n = readlink(path, buf, sizeof(buf) - 1); 88 if (n == -1) { 89 if (log) { 90 DLOG(WARNING) << "Failed to read the inode number for a socket from /proc" 91 "(" << errno << ")"; 92 } 93 return false; 94 } 95 buf[n] = 0; 96 97 if (memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1)) { 98 if (log) { 99 DLOG(WARNING) << "The descriptor passed from the crashing process wasn't " 100 " a UNIX domain socket."; 101 } 102 return false; 103 } 104 105 char* endptr; 106 const unsigned long long int inode_ul = 107 strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10); 108 if (*endptr != ']') 109 return false; 110 111 if (inode_ul == ULLONG_MAX) { 112 if (log) { 113 DLOG(WARNING) << "Failed to parse a socket's inode number: the number " 114 "was too large. Please report this bug: " << buf; 115 } 116 return false; 117 } 118 119 *inode_out = inode_ul; 120 return true; 121 } 122 123 } // namespace 124 125 namespace base { 126 127 const char kFindInodeSwitch[] = "--find-inode"; 128 129 // Account for the terminating null character. 130 static const int kDistroSize = 128 + 1; 131 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) 136 "CrOS"; 137 #elif defined(OS_ANDROID) 138 "Android"; 139 #else // if defined(OS_LINUX) 140 "Unknown"; 141 #endif 142 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"); 160 std::string output; 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))); 167 } 168 } 169 distro_state_singleton->CheckFinished(); 170 return g_linux_distro; 171 #else 172 NOTIMPLEMENTED(); 173 return "Unknown"; 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 DLOG(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 = static_cast<ino_t>(-1); 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 bool* syscall_supported) { 257 char buf[256]; 258 snprintf(buf, sizeof(buf), "/proc/%d/task", pid); 259 260 if (syscall_supported != NULL) 261 *syscall_supported = false; 262 263 DIR* task = opendir(buf); 264 if (!task) { 265 DLOG(WARNING) << "Cannot open " << buf; 266 return -1; 267 } 268 269 std::vector<pid_t> tids; 270 struct dirent* dent; 271 while ((dent = readdir(task))) { 272 char* endptr; 273 const unsigned long int tid_ul = strtoul(dent->d_name, &endptr, 10); 274 if (tid_ul == ULONG_MAX || *endptr) 275 continue; 276 tids.push_back(tid_ul); 277 } 278 closedir(task); 279 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); 286 if (fd < 0) 287 continue; 288 if (syscall_supported != NULL) 289 *syscall_supported = true; 290 bool read_ret = ReadFromFD(fd, syscall_data.get(), expected_data.length()); 291 close(fd); 292 if (!read_ret) 293 continue; 294 295 if (0 == strncmp(expected_data.c_str(), syscall_data.get(), 296 expected_data.length())) { 297 return current_tid; 298 } 299 } 300 return -1; 301 } 302 303 } // namespace base 304