1 // Copyright (c) 2009 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 <glib.h> 10 #include <stdlib.h> 11 #include <sys/stat.h> 12 #include <unistd.h> 13 14 #include <vector> 15 16 #include "base/command_line.h" 17 #include "base/lock.h" 18 #include "base/path_service.h" 19 #include "base/process_util.h" 20 #include "base/singleton.h" 21 #include "base/string_util.h" 22 #include "base/third_party/xdg_user_dirs/xdg_user_dir_lookup.h" 23 24 namespace { 25 26 class EnvironmentVariableGetterImpl 27 : public base::EnvironmentVariableGetter { 28 public: 29 virtual bool Getenv(const char* variable_name, std::string* result) { 30 const char* env_value = ::getenv(variable_name); 31 if (env_value) { 32 // Note that the variable may be defined but empty. 33 *result = env_value; 34 return true; 35 } 36 // Some commonly used variable names are uppercase while others 37 // are lowercase, which is inconsistent. Let's try to be helpful 38 // and look for a variable name with the reverse case. 39 char first_char = variable_name[0]; 40 std::string alternate_case_var; 41 if (first_char >= 'a' && first_char <= 'z') 42 alternate_case_var = StringToUpperASCII(std::string(variable_name)); 43 else if (first_char >= 'A' && first_char <= 'Z') 44 alternate_case_var = StringToLowerASCII(std::string(variable_name)); 45 else 46 return false; 47 env_value = ::getenv(alternate_case_var.c_str()); 48 if (env_value) { 49 *result = env_value; 50 return true; 51 } 52 return false; 53 } 54 }; 55 56 // Not needed for OS_CHROMEOS. 57 #if defined(OS_LINUX) 58 enum LinuxDistroState { 59 STATE_DID_NOT_CHECK = 0, 60 STATE_CHECK_STARTED = 1, 61 STATE_CHECK_FINISHED = 2, 62 }; 63 64 // Helper class for GetLinuxDistro(). 65 class LinuxDistroHelper { 66 public: 67 // Retrieves the Singleton. 68 static LinuxDistroHelper* Get() { 69 return Singleton<LinuxDistroHelper>::get(); 70 } 71 72 // The simple state machine goes from: 73 // STATE_DID_NOT_CHECK -> STATE_CHECK_STARTED -> STATE_CHECK_FINISHED. 74 LinuxDistroHelper() : state_(STATE_DID_NOT_CHECK) {} 75 ~LinuxDistroHelper() {} 76 77 // Retrieve the current state, if we're in STATE_DID_NOT_CHECK, 78 // we automatically move to STATE_CHECK_STARTED so nobody else will 79 // do the check. 80 LinuxDistroState State() { 81 AutoLock scoped_lock(lock_); 82 if (STATE_DID_NOT_CHECK == state_) { 83 state_ = STATE_CHECK_STARTED; 84 return STATE_DID_NOT_CHECK; 85 } 86 return state_; 87 } 88 89 // Indicate the check finished, move to STATE_CHECK_FINISHED. 90 void CheckFinished() { 91 AutoLock scoped_lock(lock_); 92 DCHECK(state_ == STATE_CHECK_STARTED); 93 state_ = STATE_CHECK_FINISHED; 94 } 95 96 private: 97 Lock lock_; 98 LinuxDistroState state_; 99 }; 100 #endif // if defined(OS_LINUX) 101 102 // expected prefix of the target of the /proc/self/fd/%d link for a socket 103 static const char kSocketLinkPrefix[] = "socket:["; 104 105 // Parse a symlink in /proc/pid/fd/$x and return the inode number of the 106 // socket. 107 // inode_out: (output) set to the inode number on success 108 // path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor) 109 // log: if true, log messages about failure details 110 bool ProcPathGetInode(ino_t* inode_out, const char* path, bool log = false) { 111 DCHECK(inode_out); 112 DCHECK(path); 113 114 char buf[256]; 115 const ssize_t n = readlink(path, buf, sizeof(buf) - 1); 116 if (n == -1) { 117 if (log) { 118 LOG(WARNING) << "Failed to read the inode number for a socket from /proc" 119 "(" << errno << ")"; 120 } 121 return false; 122 } 123 buf[n] = 0; 124 125 if (memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1)) { 126 if (log) { 127 LOG(WARNING) << "The descriptor passed from the crashing process wasn't a" 128 " UNIX domain socket."; 129 } 130 return false; 131 } 132 133 char *endptr; 134 const unsigned long long int inode_ul = 135 strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10); 136 if (*endptr != ']') 137 return false; 138 139 if (inode_ul == ULLONG_MAX) { 140 if (log) { 141 LOG(WARNING) << "Failed to parse a socket's inode number: the number was " 142 "too large. Please report this bug: " << buf; 143 } 144 return false; 145 } 146 147 *inode_out = inode_ul; 148 return true; 149 } 150 151 } // anonymous namespace 152 153 namespace base { 154 155 uint8_t* BGRAToRGBA(const uint8_t* pixels, int width, int height, int stride) { 156 if (stride == 0) 157 stride = width * 4; 158 159 uint8_t* new_pixels = static_cast<uint8_t*>(malloc(height * stride)); 160 161 // We have to copy the pixels and swap from BGRA to RGBA. 162 for (int i = 0; i < height; ++i) { 163 for (int j = 0; j < width; ++j) { 164 int idx = i * stride + j * 4; 165 new_pixels[idx] = pixels[idx + 2]; 166 new_pixels[idx + 1] = pixels[idx + 1]; 167 new_pixels[idx + 2] = pixels[idx]; 168 new_pixels[idx + 3] = pixels[idx + 3]; 169 } 170 } 171 172 return new_pixels; 173 } 174 175 // We use this static string to hold the Linux distro info. If we 176 // crash, the crash handler code will send this in the crash dump. 177 std::string linux_distro = 178 #if defined(OS_CHROMEOS) 179 "CrOS"; 180 #else // if defined(OS_LINUX) 181 "Unknown"; 182 #endif 183 184 FilePath GetHomeDir(EnvironmentVariableGetter* env) { 185 std::string home_dir; 186 if (env->Getenv("HOME", &home_dir) && !home_dir.empty()) 187 return FilePath(home_dir); 188 189 home_dir = g_get_home_dir(); 190 if (!home_dir.empty()) 191 return FilePath(home_dir); 192 193 FilePath rv; 194 if (PathService::Get(base::DIR_TEMP, &rv)) 195 return rv; 196 197 // Last resort. 198 return FilePath("/tmp"); 199 } 200 201 std::string GetLinuxDistro() { 202 #if defined(OS_CHROMEOS) 203 return linux_distro; 204 #else // if defined(OS_LINUX) 205 LinuxDistroHelper* distro_state_singleton = LinuxDistroHelper::Get(); 206 LinuxDistroState state = distro_state_singleton->State(); 207 if (STATE_DID_NOT_CHECK == state) { 208 // We do this check only once per process. If it fails, there's 209 // little reason to believe it will work if we attempt to run 210 // lsb_release again. 211 std::vector<std::string> argv; 212 argv.push_back("lsb_release"); 213 argv.push_back("-d"); 214 std::string output; 215 base::GetAppOutput(CommandLine(argv), &output); 216 if (output.length() > 0) { 217 // lsb_release -d should return: Description:<tab>Distro Info 218 static const std::string field = "Description:\t"; 219 if (output.compare(0, field.length(), field) == 0) { 220 linux_distro = output.substr(field.length()); 221 TrimWhitespaceASCII(linux_distro, TRIM_ALL, &linux_distro); 222 } 223 } 224 distro_state_singleton->CheckFinished(); 225 return linux_distro; 226 } else if (STATE_CHECK_STARTED == state) { 227 // If the distro check above is in progress in some other thread, we're 228 // not going to wait for the results. 229 return "Unknown"; 230 } else { 231 // In STATE_CHECK_FINISHED, no more writing to |linux_distro|. 232 return linux_distro; 233 } 234 #endif 235 } 236 237 FilePath GetXDGDirectory(EnvironmentVariableGetter* env, 238 const char* env_name, const char* fallback_dir) { 239 std::string env_value; 240 if (env->Getenv(env_name, &env_value) && !env_value.empty()) 241 return FilePath(env_value); 242 return GetHomeDir(env).Append(fallback_dir); 243 } 244 245 FilePath GetXDGUserDirectory(EnvironmentVariableGetter* env, 246 const char* dir_name, const char* fallback_dir) { 247 char* xdg_dir = xdg_user_dir_lookup(dir_name); 248 if (xdg_dir) { 249 FilePath rv(xdg_dir); 250 free(xdg_dir); 251 return rv; 252 } 253 return GetHomeDir(env).Append(fallback_dir); 254 } 255 256 // static 257 EnvironmentVariableGetter* EnvironmentVariableGetter::Create() { 258 return new EnvironmentVariableGetterImpl(); 259 } 260 261 DesktopEnvironment GetDesktopEnvironment(EnvironmentVariableGetter* env) { 262 std::string desktop_session; 263 if (env->Getenv("DESKTOP_SESSION", &desktop_session)) { 264 if (desktop_session == "gnome") 265 return DESKTOP_ENVIRONMENT_GNOME; 266 else if (desktop_session == "kde4") 267 return DESKTOP_ENVIRONMENT_KDE4; 268 else if (desktop_session == "kde") { 269 // This may mean KDE4 on newer systems, so we have to check. 270 std::string dummy; 271 if (env->Getenv("KDE_SESSION_VERSION", &dummy)) 272 return DESKTOP_ENVIRONMENT_KDE4; 273 return DESKTOP_ENVIRONMENT_KDE3; 274 } 275 } 276 277 // Fall back on some older environment variables. 278 // Useful particularly in the DESKTOP_SESSION=default case. 279 std::string dummy; 280 if (env->Getenv("GNOME_DESKTOP_SESSION_ID", &dummy)) { 281 return DESKTOP_ENVIRONMENT_GNOME; 282 } else if (env->Getenv("KDE_FULL_SESSION", &dummy)) { 283 if (env->Getenv("KDE_SESSION_VERSION", &dummy)) 284 return DESKTOP_ENVIRONMENT_KDE4; 285 return DESKTOP_ENVIRONMENT_KDE3; 286 } 287 288 return DESKTOP_ENVIRONMENT_OTHER; 289 } 290 291 const char* GetDesktopEnvironmentName(DesktopEnvironment env) { 292 switch (env) { 293 case DESKTOP_ENVIRONMENT_OTHER: 294 return NULL; 295 case DESKTOP_ENVIRONMENT_GNOME: 296 return "GNOME"; 297 case DESKTOP_ENVIRONMENT_KDE3: 298 return "KDE3"; 299 case DESKTOP_ENVIRONMENT_KDE4: 300 return "KDE4"; 301 } 302 return NULL; 303 } 304 305 const char* GetDesktopEnvironmentName(EnvironmentVariableGetter* env) { 306 return GetDesktopEnvironmentName(GetDesktopEnvironment(env)); 307 } 308 309 bool FileDescriptorGetInode(ino_t* inode_out, int fd) { 310 DCHECK(inode_out); 311 312 struct stat buf; 313 if (fstat(fd, &buf) < 0) 314 return false; 315 316 if (!S_ISSOCK(buf.st_mode)) 317 return false; 318 319 *inode_out = buf.st_ino; 320 return true; 321 } 322 323 bool FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode) { 324 DCHECK(pid_out); 325 bool already_found = false; 326 327 DIR* proc = opendir("/proc"); 328 if (!proc) { 329 LOG(WARNING) << "Cannot open /proc"; 330 return false; 331 } 332 333 std::vector<pid_t> pids; 334 335 struct dirent* dent; 336 while ((dent = readdir(proc))) { 337 char *endptr; 338 const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10); 339 if (pid_ul == ULONG_MAX || *endptr) 340 continue; 341 pids.push_back(pid_ul); 342 } 343 closedir(proc); 344 345 for (std::vector<pid_t>::const_iterator 346 i = pids.begin(); i != pids.end(); ++i) { 347 const pid_t current_pid = *i; 348 char buf[256]; 349 snprintf(buf, sizeof(buf), "/proc/%d/fd", current_pid); 350 DIR* fd = opendir(buf); 351 if (!fd) 352 continue; 353 354 while ((dent = readdir(fd))) { 355 if (snprintf(buf, sizeof(buf), "/proc/%d/fd/%s", current_pid, 356 dent->d_name) >= static_cast<int>(sizeof(buf))) { 357 continue; 358 } 359 360 ino_t fd_inode; 361 if (ProcPathGetInode(&fd_inode, buf)) { 362 if (fd_inode == socket_inode) { 363 if (already_found) { 364 closedir(fd); 365 return false; 366 } 367 368 already_found = true; 369 *pid_out = current_pid; 370 break; 371 } 372 } 373 } 374 375 closedir(fd); 376 } 377 378 return already_found; 379 } 380 381 } // namespace base 382