linux_util.cc 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  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. #include "base/linux_util.h"
  5. #include <dirent.h>
  6. #include <errno.h>
  7. #include <fcntl.h>
  8. #include <limits.h>
  9. #include <stdlib.h>
  10. #include <sys/stat.h>
  11. #include <sys/types.h>
  12. #include <unistd.h>
  13. #include <iomanip>
  14. #include <memory>
  15. #include "base/base_export.h"
  16. #include "base/files/dir_reader_posix.h"
  17. #include "base/files/file_util.h"
  18. #include "base/files/scoped_file.h"
  19. #include "base/strings/safe_sprintf.h"
  20. #include "base/strings/string_number_conversions.h"
  21. #include "base/strings/string_split.h"
  22. #include "base/strings/string_tokenizer.h"
  23. #include "base/strings/string_util.h"
  24. #include "build/build_config.h"
  25. #include "build/chromeos_buildflags.h"
  26. namespace base {
  27. namespace {
  28. #if !BUILDFLAG(IS_CHROMEOS_ASH)
  29. std::string GetKeyValueFromOSReleaseFile(const std::string& input,
  30. const char* key) {
  31. StringPairs key_value_pairs;
  32. SplitStringIntoKeyValuePairs(input, '=', '\n', &key_value_pairs);
  33. for (const auto& pair : key_value_pairs) {
  34. const std::string& key_str = pair.first;
  35. const std::string& value_str = pair.second;
  36. if (key_str == key) {
  37. // It can contain quoted characters.
  38. std::stringstream ss;
  39. std::string pretty_name;
  40. ss << value_str;
  41. // Quoted with a single tick?
  42. if (value_str[0] == '\'')
  43. ss >> std::quoted(pretty_name, '\'');
  44. else
  45. ss >> std::quoted(pretty_name);
  46. return pretty_name;
  47. }
  48. }
  49. return "";
  50. }
  51. bool ReadDistroFromOSReleaseFile(const char* file) {
  52. static const char kPrettyName[] = "PRETTY_NAME";
  53. std::string os_release_content;
  54. if (!ReadFileToString(FilePath(file), &os_release_content))
  55. return false;
  56. std::string pretty_name =
  57. GetKeyValueFromOSReleaseFile(os_release_content, kPrettyName);
  58. if (pretty_name.empty())
  59. return false;
  60. SetLinuxDistro(pretty_name);
  61. return true;
  62. }
  63. // https://www.freedesktop.org/software/systemd/man/os-release.html
  64. class DistroNameGetter {
  65. public:
  66. DistroNameGetter() {
  67. static const char* const kFilesToCheck[] = {"/etc/os-release",
  68. "/usr/lib/os-release"};
  69. for (const char* file : kFilesToCheck) {
  70. if (ReadDistroFromOSReleaseFile(file))
  71. return;
  72. }
  73. }
  74. };
  75. #endif // !BUILDFLAG(IS_CHROMEOS_ASH)
  76. // Account for the terminating null character.
  77. constexpr int kDistroSize = 128 + 1;
  78. } // namespace
  79. // We use this static string to hold the Linux distro info. If we
  80. // crash, the crash handler code will send this in the crash dump.
  81. char g_linux_distro[kDistroSize] =
  82. #if BUILDFLAG(IS_CHROMEOS_ASH)
  83. "CrOS";
  84. #elif BUILDFLAG(IS_ANDROID)
  85. "Android";
  86. #else
  87. "Unknown";
  88. #endif
  89. // This function is only supposed to be used in tests. The declaration in the
  90. // header file is guarded by "#if defined(UNIT_TEST)" so that they can be used
  91. // by tests but not non-test code. However, this .cc file is compiled as part
  92. // of "base" where "UNIT_TEST" is not defined. So we need to specify
  93. // "BASE_EXPORT" here again so that they are visible to tests.
  94. BASE_EXPORT std::string GetKeyValueFromOSReleaseFileForTesting(
  95. const std::string& input,
  96. const char* key) {
  97. #if !BUILDFLAG(IS_CHROMEOS_ASH)
  98. return GetKeyValueFromOSReleaseFile(input, key);
  99. #else
  100. return "";
  101. #endif // !BUILDFLAG(IS_CHROMEOS_ASH)
  102. }
  103. std::string GetLinuxDistro() {
  104. #if !BUILDFLAG(IS_CHROMEOS_ASH)
  105. // We do this check only once per process. If it fails, there's
  106. // little reason to believe it will work if we attempt to run it again.
  107. static DistroNameGetter distro_name_getter;
  108. #endif
  109. return g_linux_distro;
  110. }
  111. void SetLinuxDistro(const std::string& distro) {
  112. std::string trimmed_distro;
  113. TrimWhitespaceASCII(distro, TRIM_ALL, &trimmed_distro);
  114. strlcpy(g_linux_distro, trimmed_distro.c_str(), kDistroSize);
  115. }
  116. bool GetThreadsForProcess(pid_t pid, std::vector<pid_t>* tids) {
  117. // 25 > strlen("/proc//task") + strlen(std::to_string(INT_MAX)) + 1 = 22
  118. char buf[25];
  119. strings::SafeSPrintf(buf, "/proc/%d/task", pid);
  120. DirReaderPosix dir_reader(buf);
  121. if (!dir_reader.IsValid()) {
  122. DLOG(WARNING) << "Cannot open " << buf;
  123. return false;
  124. }
  125. while (dir_reader.Next()) {
  126. pid_t tid;
  127. if (StringToInt(dir_reader.name(), &tid))
  128. tids->push_back(tid);
  129. }
  130. return true;
  131. }
  132. pid_t FindThreadIDWithSyscall(pid_t pid, const std::string& expected_data,
  133. bool* syscall_supported) {
  134. if (syscall_supported)
  135. *syscall_supported = false;
  136. std::vector<pid_t> tids;
  137. if (!GetThreadsForProcess(pid, &tids))
  138. return -1;
  139. std::vector<char> syscall_data(expected_data.size());
  140. for (pid_t tid : tids) {
  141. char buf[256];
  142. snprintf(buf, sizeof(buf), "/proc/%d/task/%d/syscall", pid, tid);
  143. ScopedFD fd(open(buf, O_RDONLY));
  144. if (!fd.is_valid())
  145. continue;
  146. *syscall_supported = true;
  147. if (!ReadFromFD(fd.get(), syscall_data.data(), syscall_data.size()))
  148. continue;
  149. if (0 == strncmp(expected_data.c_str(), syscall_data.data(),
  150. expected_data.size())) {
  151. return tid;
  152. }
  153. }
  154. return -1;
  155. }
  156. pid_t FindThreadID(pid_t pid, pid_t ns_tid, bool* ns_pid_supported) {
  157. *ns_pid_supported = false;
  158. std::vector<pid_t> tids;
  159. if (!GetThreadsForProcess(pid, &tids))
  160. return -1;
  161. for (pid_t tid : tids) {
  162. char buf[256];
  163. snprintf(buf, sizeof(buf), "/proc/%d/task/%d/status", pid, tid);
  164. std::string status;
  165. if (!ReadFileToString(FilePath(buf), &status))
  166. return -1;
  167. StringTokenizer tokenizer(status, "\n");
  168. while (tokenizer.GetNext()) {
  169. StringPiece value_str(tokenizer.token_piece());
  170. if (!StartsWith(value_str, "NSpid"))
  171. continue;
  172. *ns_pid_supported = true;
  173. std::vector<StringPiece> split_value_str = SplitStringPiece(
  174. value_str, "\t", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
  175. DCHECK_GE(split_value_str.size(), 2u);
  176. int value;
  177. // The last value in the list is the PID in the namespace.
  178. if (StringToInt(split_value_str.back(), &value) && value == ns_tid) {
  179. // The second value in the list is the real PID.
  180. if (StringToInt(split_value_str[1], &value))
  181. return value;
  182. }
  183. break;
  184. }
  185. }
  186. return -1;
  187. }
  188. } // namespace base