diff options
Diffstat (limited to 'chromium/base/linux_util.cc')
-rw-r--r-- | chromium/base/linux_util.cc | 99 |
1 files changed, 78 insertions, 21 deletions
diff --git a/chromium/base/linux_util.cc b/chromium/base/linux_util.cc index caf471a39e1..2da24e1d89b 100644 --- a/chromium/base/linux_util.cc +++ b/chromium/base/linux_util.cc @@ -13,6 +13,7 @@ #include <sys/types.h> #include <unistd.h> +#include <iomanip> #include <memory> #include "base/command_line.h" @@ -28,6 +29,8 @@ #include "base/synchronization/lock.h" #include "build/build_config.h" +namespace base { + namespace { // Not needed for OS_CHROMEOS. @@ -43,7 +46,7 @@ class LinuxDistroHelper { public: // Retrieves the Singleton. static LinuxDistroHelper* GetInstance() { - return base::Singleton<LinuxDistroHelper>::get(); + return Singleton<LinuxDistroHelper>::get(); } // The simple state machine goes from: @@ -55,7 +58,7 @@ class LinuxDistroHelper { // we automatically move to STATE_CHECK_STARTED so nobody else will // do the check. LinuxDistroState State() { - base::AutoLock scoped_lock(lock_); + AutoLock scoped_lock(lock_); if (STATE_DID_NOT_CHECK == state_) { state_ = STATE_CHECK_STARTED; return STATE_DID_NOT_CHECK; @@ -65,21 +68,72 @@ class LinuxDistroHelper { // Indicate the check finished, move to STATE_CHECK_FINISHED. void CheckFinished() { - base::AutoLock scoped_lock(lock_); + AutoLock scoped_lock(lock_); DCHECK_EQ(STATE_CHECK_STARTED, state_); state_ = STATE_CHECK_FINISHED; } private: - base::Lock lock_; + Lock lock_; LinuxDistroState state_; }; + +#if !defined(OS_CHROMEOS) +std::string GetKeyValueFromOSReleaseFile(const std::string& input, + const char* key) { + StringPairs key_value_pairs; + SplitStringIntoKeyValuePairs(input, '=', '\n', &key_value_pairs); + for (const auto& pair : key_value_pairs) { + const std::string& key_str = pair.first; + const std::string& value_str = pair.second; + if (key_str == key) { + // It can contain quoted characters. + std::stringstream ss; + std::string pretty_name; + ss << value_str; + // Quoted with a single tick? + if (value_str[0] == '\'') + ss >> std::quoted(pretty_name, '\''); + else + ss >> std::quoted(pretty_name); + + return pretty_name; + } + } + + return ""; +} + +bool ReadDistroFromOSReleaseFile(const char* file) { + static const char kPrettyName[] = "PRETTY_NAME"; + + std::string os_release_content; + if (!ReadFileToString(FilePath(file), &os_release_content)) + return false; + + std::string pretty_name = + GetKeyValueFromOSReleaseFile(os_release_content, kPrettyName); + if (pretty_name.empty()) + return false; + + SetLinuxDistro(pretty_name); + return true; +} + +// https://www.freedesktop.org/software/systemd/man/os-release.html +void GetDistroNameFromOSRelease() { + static const char* const kFilesToCheck[] = {"/etc/os-release", + "/usr/lib/os-release"}; + for (const char* file : kFilesToCheck) { + if (ReadDistroFromOSReleaseFile(file)) + return; + } +} +#endif // if !defined(OS_CHROMEOS) #endif // if defined(OS_LINUX) } // namespace -namespace base { - // Account for the terminating null character. static const int kDistroSize = 128 + 1; @@ -94,6 +148,21 @@ char g_linux_distro[kDistroSize] = "Unknown"; #endif +// This function is only supposed to be used in tests. The declaration in the +// header file is guarded by "#if defined(UNIT_TEST)" so that they can be used +// by tests but not non-test code. However, this .cc file is compiled as part +// of "base" where "UNIT_TEST" is not defined. So we need to specify +// "BASE_EXPORT" here again so that they are visible to tests. +BASE_EXPORT std::string GetKeyValueFromOSReleaseFileForTesting( + const std::string& input, + const char* key) { +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + return GetKeyValueFromOSReleaseFile(input, key); +#else + return ""; +#endif // defined(OS_LINUX) && !defined(OS_CHROMEOS) +} + std::string GetLinuxDistro() { #if defined(OS_CHROMEOS) || defined(OS_ANDROID) return g_linux_distro; @@ -106,20 +175,8 @@ std::string GetLinuxDistro() { return "Unknown"; // Don't wait for other thread to finish. DCHECK_EQ(state, STATE_DID_NOT_CHECK); // We do this check only once per process. If it fails, there's - // little reason to believe it will work if we attempt to run - // lsb_release again. - std::vector<std::string> argv; - argv.push_back("lsb_release"); - argv.push_back("-d"); - std::string output; - GetAppOutput(CommandLine(argv), &output); - if (output.length() > 0) { - // lsb_release -d should return: Description:<tab>Distro Info - const char field[] = "Description:\t"; - if (output.compare(0, strlen(field), field) == 0) { - SetLinuxDistro(output.substr(strlen(field))); - } - } + // little reason to believe it will work if we attempt to run it again. + GetDistroNameFromOSRelease(); distro_state_singleton->CheckFinished(); return g_linux_distro; #else @@ -137,7 +194,7 @@ void SetLinuxDistro(const std::string& distro) { bool GetThreadsForProcess(pid_t pid, std::vector<pid_t>* tids) { // 25 > strlen("/proc//task") + strlen(std::to_string(INT_MAX)) + 1 = 22 char buf[25]; - base::strings::SafeSPrintf(buf, "/proc/%d/task", pid); + strings::SafeSPrintf(buf, "/proc/%d/task", pid); DirReaderPosix dir_reader(buf); if (!dir_reader.IsValid()) { |