15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/linux_util.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <dirent.h> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <errno.h> 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <fcntl.h> 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdlib.h> 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <sys/stat.h> 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <sys/types.h> 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <unistd.h> 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector> 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/command_line.h" 186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "base/files/file_util.h" 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_ptr.h" 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/singleton.h" 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/path_service.h" 2258e6fbe4ee35d65e14b626c557d37565bf8ad179Ben Murdoch#include "base/process/launch.h" 23868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h" 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/synchronization/lock.h" 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Not needed for OS_CHROMEOS. 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_LINUX) 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)enum LinuxDistroState { 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) STATE_DID_NOT_CHECK = 0, 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) STATE_CHECK_STARTED = 1, 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) STATE_CHECK_FINISHED = 2, 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Helper class for GetLinuxDistro(). 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class LinuxDistroHelper { 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public: 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Retrieves the Singleton. 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static LinuxDistroHelper* GetInstance() { 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return Singleton<LinuxDistroHelper>::get(); 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The simple state machine goes from: 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // STATE_DID_NOT_CHECK -> STATE_CHECK_STARTED -> STATE_CHECK_FINISHED. 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LinuxDistroHelper() : state_(STATE_DID_NOT_CHECK) {} 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ~LinuxDistroHelper() {} 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Retrieve the current state, if we're in STATE_DID_NOT_CHECK, 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // we automatically move to STATE_CHECK_STARTED so nobody else will 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // do the check. 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LinuxDistroState State() { 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::AutoLock scoped_lock(lock_); 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (STATE_DID_NOT_CHECK == state_) { 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) state_ = STATE_CHECK_STARTED; 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return STATE_DID_NOT_CHECK; 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return state_; 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Indicate the check finished, move to STATE_CHECK_FINISHED. 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void CheckFinished() { 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::AutoLock scoped_lock(lock_); 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_EQ(STATE_CHECK_STARTED, state_); 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) state_ = STATE_CHECK_FINISHED; 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private: 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Lock lock_; 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LinuxDistroState state_; 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif // if defined(OS_LINUX) 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace base { 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Account for the terminating null character. 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kDistroSize = 128 + 1; 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// We use this static string to hold the Linux distro info. If we 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// crash, the crash handler code will send this in the crash dump. 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)char g_linux_distro[kDistroSize] = 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_CHROMEOS) 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "CrOS"; 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif defined(OS_ANDROID) 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "Android"; 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else // if defined(OS_LINUX) 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "Unknown"; 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string GetLinuxDistro() { 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_CHROMEOS) || defined(OS_ANDROID) 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return g_linux_distro; 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif defined(OS_LINUX) 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LinuxDistroHelper* distro_state_singleton = LinuxDistroHelper::GetInstance(); 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LinuxDistroState state = distro_state_singleton->State(); 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (STATE_CHECK_FINISHED == state) 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return g_linux_distro; 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (STATE_CHECK_STARTED == state) 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "Unknown"; // Don't wait for other thread to finish. 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_EQ(state, STATE_DID_NOT_CHECK); 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We do this check only once per process. If it fails, there's 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // little reason to believe it will work if we attempt to run 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // lsb_release again. 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::vector<std::string> argv; 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) argv.push_back("lsb_release"); 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) argv.push_back("-d"); 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string output; 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::GetAppOutput(CommandLine(argv), &output); 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (output.length() > 0) { 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // lsb_release -d should return: Description:<tab>Distro Info 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const char field[] = "Description:\t"; 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (output.compare(0, strlen(field), field) == 0) { 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetLinuxDistro(output.substr(strlen(field))); 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) distro_state_singleton->CheckFinished(); 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return g_linux_distro; 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTIMPLEMENTED(); 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "Unknown"; 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SetLinuxDistro(const std::string& distro) { 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string trimmed_distro; 128a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base::TrimWhitespaceASCII(distro, base::TRIM_ALL, &trimmed_distro); 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::strlcpy(g_linux_distro, trimmed_distro.c_str(), kDistroSize); 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)pid_t FindThreadIDWithSyscall(pid_t pid, const std::string& expected_data, 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool* syscall_supported) { 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) char buf[256]; 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) snprintf(buf, sizeof(buf), "/proc/%d/task", pid); 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (syscall_supported != NULL) 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *syscall_supported = false; 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DIR* task = opendir(buf); 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!task) { 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DLOG(WARNING) << "Cannot open " << buf; 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return -1; 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::vector<pid_t> tids; 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) struct dirent* dent; 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while ((dent = readdir(task))) { 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) char* endptr; 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const unsigned long int tid_ul = strtoul(dent->d_name, &endptr, 10); 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (tid_ul == ULONG_MAX || *endptr) 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue; 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) tids.push_back(tid_ul); 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) closedir(task); 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) scoped_ptr<char[]> syscall_data(new char[expected_data.length()]); 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (std::vector<pid_t>::const_iterator 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) i = tids.begin(); i != tids.end(); ++i) { 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const pid_t current_tid = *i; 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) snprintf(buf, sizeof(buf), "/proc/%d/task/%d/syscall", pid, current_tid); 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int fd = open(buf, O_RDONLY); 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (fd < 0) 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue; 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (syscall_supported != NULL) 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *syscall_supported = true; 167f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) bool read_ret = ReadFromFD(fd, syscall_data.get(), expected_data.length()); 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) close(fd); 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!read_ret) 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue; 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (0 == strncmp(expected_data.c_str(), syscall_data.get(), 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) expected_data.length())) { 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return current_tid; 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return -1; 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace base 181