123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "base/linux_util.h"
- #include <dirent.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <limits.h>
- #include <stdlib.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <iomanip>
- #include <memory>
- #include "base/base_export.h"
- #include "base/files/dir_reader_posix.h"
- #include "base/files/file_util.h"
- #include "base/files/scoped_file.h"
- #include "base/strings/safe_sprintf.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/strings/string_split.h"
- #include "base/strings/string_tokenizer.h"
- #include "base/strings/string_util.h"
- #include "build/build_config.h"
- #include "build/chromeos_buildflags.h"
- namespace base {
- namespace {
- #if !BUILDFLAG(IS_CHROMEOS_ASH)
- 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
- class DistroNameGetter {
- public:
- DistroNameGetter() {
- static const char* const kFilesToCheck[] = {"/etc/os-release",
- "/usr/lib/os-release"};
- for (const char* file : kFilesToCheck) {
- if (ReadDistroFromOSReleaseFile(file))
- return;
- }
- }
- };
- #endif // !BUILDFLAG(IS_CHROMEOS_ASH)
- // Account for the terminating null character.
- constexpr int kDistroSize = 128 + 1;
- } // namespace
- // We use this static string to hold the Linux distro info. If we
- // crash, the crash handler code will send this in the crash dump.
- char g_linux_distro[kDistroSize] =
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- "CrOS";
- #elif BUILDFLAG(IS_ANDROID)
- "Android";
- #else
- "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 !BUILDFLAG(IS_CHROMEOS_ASH)
- return GetKeyValueFromOSReleaseFile(input, key);
- #else
- return "";
- #endif // !BUILDFLAG(IS_CHROMEOS_ASH)
- }
- std::string GetLinuxDistro() {
- #if !BUILDFLAG(IS_CHROMEOS_ASH)
- // 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 it again.
- static DistroNameGetter distro_name_getter;
- #endif
- return g_linux_distro;
- }
- void SetLinuxDistro(const std::string& distro) {
- std::string trimmed_distro;
- TrimWhitespaceASCII(distro, TRIM_ALL, &trimmed_distro);
- strlcpy(g_linux_distro, trimmed_distro.c_str(), kDistroSize);
- }
- 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];
- strings::SafeSPrintf(buf, "/proc/%d/task", pid);
- DirReaderPosix dir_reader(buf);
- if (!dir_reader.IsValid()) {
- DLOG(WARNING) << "Cannot open " << buf;
- return false;
- }
- while (dir_reader.Next()) {
- pid_t tid;
- if (StringToInt(dir_reader.name(), &tid))
- tids->push_back(tid);
- }
- return true;
- }
- pid_t FindThreadIDWithSyscall(pid_t pid, const std::string& expected_data,
- bool* syscall_supported) {
- if (syscall_supported)
- *syscall_supported = false;
- std::vector<pid_t> tids;
- if (!GetThreadsForProcess(pid, &tids))
- return -1;
- std::vector<char> syscall_data(expected_data.size());
- for (pid_t tid : tids) {
- char buf[256];
- snprintf(buf, sizeof(buf), "/proc/%d/task/%d/syscall", pid, tid);
- ScopedFD fd(open(buf, O_RDONLY));
- if (!fd.is_valid())
- continue;
- *syscall_supported = true;
- if (!ReadFromFD(fd.get(), syscall_data.data(), syscall_data.size()))
- continue;
- if (0 == strncmp(expected_data.c_str(), syscall_data.data(),
- expected_data.size())) {
- return tid;
- }
- }
- return -1;
- }
- pid_t FindThreadID(pid_t pid, pid_t ns_tid, bool* ns_pid_supported) {
- *ns_pid_supported = false;
- std::vector<pid_t> tids;
- if (!GetThreadsForProcess(pid, &tids))
- return -1;
- for (pid_t tid : tids) {
- char buf[256];
- snprintf(buf, sizeof(buf), "/proc/%d/task/%d/status", pid, tid);
- std::string status;
- if (!ReadFileToString(FilePath(buf), &status))
- return -1;
- StringTokenizer tokenizer(status, "\n");
- while (tokenizer.GetNext()) {
- StringPiece value_str(tokenizer.token_piece());
- if (!StartsWith(value_str, "NSpid"))
- continue;
- *ns_pid_supported = true;
- std::vector<StringPiece> split_value_str = SplitStringPiece(
- value_str, "\t", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
- DCHECK_GE(split_value_str.size(), 2u);
- int value;
- // The last value in the list is the PID in the namespace.
- if (StringToInt(split_value_str.back(), &value) && value == ns_tid) {
- // The second value in the list is the real PID.
- if (StringToInt(split_value_str[1], &value))
- return value;
- }
- break;
- }
- }
- return -1;
- }
- } // namespace base
|