Avi Drissman | e4622aa | 2022-09-08 20:36:06 | [diff] [blame] | 1 | // Copyright 2012 The Chromium Authors |
[email protected] | 75ae542 | 2009-04-21 17:20:10 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Tom Sepez | 8726d30e | 2025-01-29 02:11:08 | [diff] [blame] | 5 | #ifdef UNSAFE_BUFFERS_BUILD |
| 6 | // TODO(crbug.com/390223051): Remove C-library calls to fix the errors. |
| 7 | #pragma allow_unsafe_libc_calls |
| 8 | #endif |
| 9 | |
[email protected] | 912c645 | 2009-07-17 05:55:51 | [diff] [blame] | 10 | #include "base/linux_util.h" |
[email protected] | 75ae542 | 2009-04-21 17:20:10 | [diff] [blame] | 11 | |
[email protected] | 85ebe8f | 2009-10-29 04:02:55 | [diff] [blame] | 12 | #include <dirent.h> |
| 13 | #include <errno.h> |
[email protected] | 66218314 | 2010-07-16 19:28:17 | [diff] [blame] | 14 | #include <fcntl.h> |
avi | 9b6f4293 | 2015-12-26 22:15:14 | [diff] [blame] | 15 | #include <limits.h> |
[email protected] | 75ae542 | 2009-04-21 17:20:10 | [diff] [blame] | 16 | #include <stdlib.h> |
[email protected] | 85ebe8f | 2009-10-29 04:02:55 | [diff] [blame] | 17 | #include <sys/stat.h> |
[email protected] | 66218314 | 2010-07-16 19:28:17 | [diff] [blame] | 18 | #include <sys/types.h> |
[email protected] | 85ebe8f | 2009-10-29 04:02:55 | [diff] [blame] | 19 | #include <unistd.h> |
[email protected] | 75ae542 | 2009-04-21 17:20:10 | [diff] [blame] | 20 | |
Tomas Popela | 5b9b01f | 2019-09-10 19:42:31 | [diff] [blame] | 21 | #include <iomanip> |
dcheng | 093de9b | 2016-04-04 21:25:51 | [diff] [blame] | 22 | #include <memory> |
Helmut Januschka | 91069ddb | 2024-04-09 01:42:16 | [diff] [blame] | 23 | #include <string_view> |
[email protected] | 912c645 | 2009-07-17 05:55:51 | [diff] [blame] | 24 | |
David Sanders | 6e70994 | 2022-04-05 06:49:26 | [diff] [blame] | 25 | #include "base/base_export.h" |
Alexandr Ilin | d4f4b34 | 2019-01-08 15:34:09 | [diff] [blame] | 26 | #include "base/files/dir_reader_posix.h" |
[email protected] | e3177dd5 | 2014-08-13 20:22:14 | [diff] [blame] | 27 | #include "base/files/file_util.h" |
Lei Zhang | 3bf0669 | 2020-02-03 19:41:05 | [diff] [blame] | 28 | #include "base/files/scoped_file.h" |
Alexandr Ilin | d4f4b34 | 2019-01-08 15:34:09 | [diff] [blame] | 29 | #include "base/strings/safe_sprintf.h" |
Tom Sepez | 5a0c23c4 | 2025-01-06 20:45:32 | [diff] [blame] | 30 | #include "base/strings/span_printf.h" |
reveman | 7b97c32 | 2016-09-20 02:10:58 | [diff] [blame] | 31 | #include "base/strings/string_number_conversions.h" |
| 32 | #include "base/strings/string_split.h" |
reveman | e5aece57 | 2017-09-04 07:18:33 | [diff] [blame] | 33 | #include "base/strings/string_tokenizer.h" |
[email protected] | d529cb0 | 2013-06-10 19:06:57 | [diff] [blame] | 34 | #include "base/strings/string_util.h" |
avi | 9b6f4293 | 2015-12-26 22:15:14 | [diff] [blame] | 35 | #include "build/build_config.h" |
[email protected] | 87fc168 | 2009-07-22 00:22:49 | [diff] [blame] | 36 | |
Tomas Popela | 5b9b01f | 2019-09-10 19:42:31 | [diff] [blame] | 37 | namespace base { |
| 38 | |
[email protected] | 87fc168 | 2009-07-22 00:22:49 | [diff] [blame] | 39 | namespace { |
| 40 | |
Georg Neis | ff37fb5 | 2025-02-05 09:05:26 | [diff] [blame] | 41 | #if !BUILDFLAG(IS_CHROMEOS) |
Tomas Popela | 5b9b01f | 2019-09-10 19:42:31 | [diff] [blame] | 42 | std::string GetKeyValueFromOSReleaseFile(const std::string& input, |
| 43 | const char* key) { |
| 44 | StringPairs key_value_pairs; |
| 45 | SplitStringIntoKeyValuePairs(input, '=', '\n', &key_value_pairs); |
| 46 | for (const auto& pair : key_value_pairs) { |
| 47 | const std::string& key_str = pair.first; |
| 48 | const std::string& value_str = pair.second; |
| 49 | if (key_str == key) { |
| 50 | // It can contain quoted characters. |
| 51 | std::stringstream ss; |
| 52 | std::string pretty_name; |
| 53 | ss << value_str; |
| 54 | // Quoted with a single tick? |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 55 | if (value_str[0] == '\'') { |
Tomas Popela | 5b9b01f | 2019-09-10 19:42:31 | [diff] [blame] | 56 | ss >> std::quoted(pretty_name, '\''); |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 57 | } else { |
Tomas Popela | 5b9b01f | 2019-09-10 19:42:31 | [diff] [blame] | 58 | ss >> std::quoted(pretty_name); |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 59 | } |
Tomas Popela | 5b9b01f | 2019-09-10 19:42:31 | [diff] [blame] | 60 | |
| 61 | return pretty_name; |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | return ""; |
| 66 | } |
| 67 | |
| 68 | bool ReadDistroFromOSReleaseFile(const char* file) { |
| 69 | static const char kPrettyName[] = "PRETTY_NAME"; |
| 70 | |
| 71 | std::string os_release_content; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 72 | if (!ReadFileToString(FilePath(file), &os_release_content)) { |
Tomas Popela | 5b9b01f | 2019-09-10 19:42:31 | [diff] [blame] | 73 | return false; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 74 | } |
Tomas Popela | 5b9b01f | 2019-09-10 19:42:31 | [diff] [blame] | 75 | |
| 76 | std::string pretty_name = |
| 77 | GetKeyValueFromOSReleaseFile(os_release_content, kPrettyName); |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 78 | if (pretty_name.empty()) { |
Tomas Popela | 5b9b01f | 2019-09-10 19:42:31 | [diff] [blame] | 79 | return false; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 80 | } |
Tomas Popela | 5b9b01f | 2019-09-10 19:42:31 | [diff] [blame] | 81 | |
| 82 | SetLinuxDistro(pretty_name); |
| 83 | return true; |
| 84 | } |
| 85 | |
| 86 | // https://www.freedesktop.org/software/systemd/man/os-release.html |
Tom Anderson | 02185a0 | 2020-02-01 03:49:25 | [diff] [blame] | 87 | class DistroNameGetter { |
| 88 | public: |
| 89 | DistroNameGetter() { |
| 90 | static const char* const kFilesToCheck[] = {"/etc/os-release", |
| 91 | "/usr/lib/os-release"}; |
| 92 | for (const char* file : kFilesToCheck) { |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 93 | if (ReadDistroFromOSReleaseFile(file)) { |
Tom Anderson | 02185a0 | 2020-02-01 03:49:25 | [diff] [blame] | 94 | return; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 95 | } |
Tom Anderson | 02185a0 | 2020-02-01 03:49:25 | [diff] [blame] | 96 | } |
Tomas Popela | 5b9b01f | 2019-09-10 19:42:31 | [diff] [blame] | 97 | } |
Tom Anderson | 02185a0 | 2020-02-01 03:49:25 | [diff] [blame] | 98 | }; |
Georg Neis | ff37fb5 | 2025-02-05 09:05:26 | [diff] [blame] | 99 | #endif // !BUILDFLAG(IS_CHROMEOS) |
[email protected] | 19467c0 | 2009-10-13 02:51:07 | [diff] [blame] | 100 | |
Igor Kraskevich | bfc62af | 2023-10-17 15:09:28 | [diff] [blame] | 101 | bool GetThreadsFromProcessDir(const char* dir_path, std::vector<pid_t>* tids) { |
| 102 | DirReaderPosix dir_reader(dir_path); |
| 103 | |
| 104 | if (!dir_reader.IsValid()) { |
| 105 | DLOG(WARNING) << "Cannot open " << dir_path; |
| 106 | return false; |
| 107 | } |
| 108 | |
| 109 | while (dir_reader.Next()) { |
| 110 | pid_t tid; |
| 111 | if (StringToInt(dir_reader.name(), &tid)) { |
| 112 | tids->push_back(tid); |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | return true; |
| 117 | } |
| 118 | |
[email protected] | 6dde9d7 | 2010-08-26 08:55:22 | [diff] [blame] | 119 | // Account for the terminating null character. |
Lei Zhang | 3bf0669 | 2020-02-03 19:41:05 | [diff] [blame] | 120 | constexpr int kDistroSize = 128 + 1; |
| 121 | |
| 122 | } // namespace |
[email protected] | 6dde9d7 | 2010-08-26 08:55:22 | [diff] [blame] | 123 | |
[email protected] | 912c645 | 2009-07-17 05:55:51 | [diff] [blame] | 124 | // We use this static string to hold the Linux distro info. If we |
| 125 | // crash, the crash handler code will send this in the crash dump. |
[email protected] | 6dde9d7 | 2010-08-26 08:55:22 | [diff] [blame] | 126 | char g_linux_distro[kDistroSize] = |
Georg Neis | ff37fb5 | 2025-02-05 09:05:26 | [diff] [blame] | 127 | #if BUILDFLAG(IS_CHROMEOS) |
[email protected] | 78f6f51 | 2009-10-21 19:04:17 | [diff] [blame] | 128 | "CrOS"; |
Xiaohan Wang | 38e4ebb | 2022-01-19 06:57:43 | [diff] [blame] | 129 | #elif BUILDFLAG(IS_ANDROID) |
[email protected] | 84a1ab5a | 2012-07-18 01:49:22 | [diff] [blame] | 130 | "Android"; |
Tom Anderson | 02185a0 | 2020-02-01 03:49:25 | [diff] [blame] | 131 | #else |
[email protected] | 78f6f51 | 2009-10-21 19:04:17 | [diff] [blame] | 132 | "Unknown"; |
| 133 | #endif |
[email protected] | 912c645 | 2009-07-17 05:55:51 | [diff] [blame] | 134 | |
Raphael Kubo da Costa | 71c31f5 | 2019-09-11 16:17:00 | [diff] [blame] | 135 | // This function is only supposed to be used in tests. The declaration in the |
| 136 | // header file is guarded by "#if defined(UNIT_TEST)" so that they can be used |
| 137 | // by tests but not non-test code. However, this .cc file is compiled as part |
| 138 | // of "base" where "UNIT_TEST" is not defined. So we need to specify |
| 139 | // "BASE_EXPORT" here again so that they are visible to tests. |
Tomas Popela | 5b9b01f | 2019-09-10 19:42:31 | [diff] [blame] | 140 | BASE_EXPORT std::string GetKeyValueFromOSReleaseFileForTesting( |
| 141 | const std::string& input, |
| 142 | const char* key) { |
Georg Neis | ff37fb5 | 2025-02-05 09:05:26 | [diff] [blame] | 143 | #if !BUILDFLAG(IS_CHROMEOS) |
Tomas Popela | 5b9b01f | 2019-09-10 19:42:31 | [diff] [blame] | 144 | return GetKeyValueFromOSReleaseFile(input, key); |
| 145 | #else |
| 146 | return ""; |
Georg Neis | ff37fb5 | 2025-02-05 09:05:26 | [diff] [blame] | 147 | #endif // !BUILDFLAG(IS_CHROMEOS) |
Tomas Popela | 5b9b01f | 2019-09-10 19:42:31 | [diff] [blame] | 148 | } |
| 149 | |
[email protected] | 912c645 | 2009-07-17 05:55:51 | [diff] [blame] | 150 | std::string GetLinuxDistro() { |
Georg Neis | ff37fb5 | 2025-02-05 09:05:26 | [diff] [blame] | 151 | #if !BUILDFLAG(IS_CHROMEOS) |
[email protected] | 84a1ab5a | 2012-07-18 01:49:22 | [diff] [blame] | 152 | // We do this check only once per process. If it fails, there's |
Tomas Popela | 5b9b01f | 2019-09-10 19:42:31 | [diff] [blame] | 153 | // little reason to believe it will work if we attempt to run it again. |
Avi Drissman | ded7717 | 2021-07-02 18:23:00 | [diff] [blame] | 154 | static DistroNameGetter distro_name_getter; |
[email protected] | 78f6f51 | 2009-10-21 19:04:17 | [diff] [blame] | 155 | #endif |
Tom Anderson | 02185a0 | 2020-02-01 03:49:25 | [diff] [blame] | 156 | return g_linux_distro; |
[email protected] | 912c645 | 2009-07-17 05:55:51 | [diff] [blame] | 157 | } |
| 158 | |
[email protected] | 6dde9d7 | 2010-08-26 08:55:22 | [diff] [blame] | 159 | void SetLinuxDistro(const std::string& distro) { |
| 160 | std::string trimmed_distro; |
olli.raula | 36aa8be | 2015-09-10 11:14:22 | [diff] [blame] | 161 | TrimWhitespaceASCII(distro, TRIM_ALL, &trimmed_distro); |
| 162 | strlcpy(g_linux_distro, trimmed_distro.c_str(), kDistroSize); |
[email protected] | 6dde9d7 | 2010-08-26 08:55:22 | [diff] [blame] | 163 | } |
| 164 | |
Alexandr Ilin | d4f4b34 | 2019-01-08 15:34:09 | [diff] [blame] | 165 | bool GetThreadsForProcess(pid_t pid, std::vector<pid_t>* tids) { |
Ho Cheung | 42097f6 | 2024-10-22 13:34:22 | [diff] [blame] | 166 | // 25 > strlen("/proc//task") + strlen(base::NumberToString(INT_MAX)) + 1 = 22 |
Alexandr Ilin | d4f4b34 | 2019-01-08 15:34:09 | [diff] [blame] | 167 | char buf[25]; |
Tomas Popela | 5b9b01f | 2019-09-10 19:42:31 | [diff] [blame] | 168 | strings::SafeSPrintf(buf, "/proc/%d/task", pid); |
Igor Kraskevich | bfc62af | 2023-10-17 15:09:28 | [diff] [blame] | 169 | return GetThreadsFromProcessDir(buf, tids); |
| 170 | } |
Alexandr Ilin | d4f4b34 | 2019-01-08 15:34:09 | [diff] [blame] | 171 | |
Igor Kraskevich | bfc62af | 2023-10-17 15:09:28 | [diff] [blame] | 172 | bool GetThreadsForCurrentProcess(std::vector<pid_t>* tids) { |
| 173 | return GetThreadsFromProcessDir("/proc/self/task", tids); |
Alexandr Ilin | d4f4b34 | 2019-01-08 15:34:09 | [diff] [blame] | 174 | } |
| 175 | |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 176 | pid_t FindThreadIDWithSyscall(pid_t pid, |
| 177 | const std::string& expected_data, |
[email protected] | cb7d53e | 2011-06-21 04:21:06 | [diff] [blame] | 178 | bool* syscall_supported) { |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 179 | if (syscall_supported) { |
[email protected] | cb7d53e | 2011-06-21 04:21:06 | [diff] [blame] | 180 | *syscall_supported = false; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 181 | } |
[email protected] | cb7d53e | 2011-06-21 04:21:06 | [diff] [blame] | 182 | |
[email protected] | 66218314 | 2010-07-16 19:28:17 | [diff] [blame] | 183 | std::vector<pid_t> tids; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 184 | if (!GetThreadsForProcess(pid, &tids)) { |
reveman | 7b97c32 | 2016-09-20 02:10:58 | [diff] [blame] | 185 | return -1; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 186 | } |
[email protected] | 66218314 | 2010-07-16 19:28:17 | [diff] [blame] | 187 | |
Lei Zhang | 3bf0669 | 2020-02-03 19:41:05 | [diff] [blame] | 188 | std::vector<char> syscall_data(expected_data.size()); |
reveman | 7b97c32 | 2016-09-20 02:10:58 | [diff] [blame] | 189 | for (pid_t tid : tids) { |
| 190 | char buf[256]; |
Tom Sepez | 5a0c23c4 | 2025-01-06 20:45:32 | [diff] [blame] | 191 | base::SpanPrintf(buf, "/proc/%d/task/%d/syscall", pid, tid); |
Lei Zhang | 3bf0669 | 2020-02-03 19:41:05 | [diff] [blame] | 192 | ScopedFD fd(open(buf, O_RDONLY)); |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 193 | if (!fd.is_valid()) { |
[email protected] | 66218314 | 2010-07-16 19:28:17 | [diff] [blame] | 194 | continue; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 195 | } |
[email protected] | 66218314 | 2010-07-16 19:28:17 | [diff] [blame] | 196 | |
Lei Zhang | 3bf0669 | 2020-02-03 19:41:05 | [diff] [blame] | 197 | *syscall_supported = true; |
Austin Sullivan | 49f9744 | 2024-01-08 20:26:27 | [diff] [blame] | 198 | if (!ReadFromFD(fd.get(), syscall_data)) { |
Lei Zhang | 3bf0669 | 2020-02-03 19:41:05 | [diff] [blame] | 199 | continue; |
Austin Sullivan | 49f9744 | 2024-01-08 20:26:27 | [diff] [blame] | 200 | } |
Lei Zhang | 3bf0669 | 2020-02-03 19:41:05 | [diff] [blame] | 201 | |
| 202 | if (0 == strncmp(expected_data.c_str(), syscall_data.data(), |
| 203 | expected_data.size())) { |
reveman | 7b97c32 | 2016-09-20 02:10:58 | [diff] [blame] | 204 | return tid; |
| 205 | } |
| 206 | } |
| 207 | return -1; |
| 208 | } |
| 209 | |
| 210 | pid_t FindThreadID(pid_t pid, pid_t ns_tid, bool* ns_pid_supported) { |
Lei Zhang | 3bf0669 | 2020-02-03 19:41:05 | [diff] [blame] | 211 | *ns_pid_supported = false; |
reveman | 7b97c32 | 2016-09-20 02:10:58 | [diff] [blame] | 212 | |
| 213 | std::vector<pid_t> tids; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 214 | if (!GetThreadsForProcess(pid, &tids)) { |
reveman | 7b97c32 | 2016-09-20 02:10:58 | [diff] [blame] | 215 | return -1; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 216 | } |
reveman | 7b97c32 | 2016-09-20 02:10:58 | [diff] [blame] | 217 | |
| 218 | for (pid_t tid : tids) { |
| 219 | char buf[256]; |
Tom Sepez | 5a0c23c4 | 2025-01-06 20:45:32 | [diff] [blame] | 220 | base::SpanPrintf(buf, "/proc/%d/task/%d/status", pid, tid); |
reveman | 7b97c32 | 2016-09-20 02:10:58 | [diff] [blame] | 221 | std::string status; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 222 | if (!ReadFileToString(FilePath(buf), &status)) { |
reveman | 7b97c32 | 2016-09-20 02:10:58 | [diff] [blame] | 223 | return -1; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 224 | } |
reveman | e5aece57 | 2017-09-04 07:18:33 | [diff] [blame] | 225 | StringTokenizer tokenizer(status, "\n"); |
Lei Zhang | 09b218e5 | 2024-09-12 19:21:55 | [diff] [blame] | 226 | while (std::optional<std::string_view> token = |
| 227 | tokenizer.GetNextTokenView()) { |
| 228 | if (!StartsWith(token.value(), "NSpid")) { |
reveman | e5aece57 | 2017-09-04 07:18:33 | [diff] [blame] | 229 | continue; |
Lei Zhang | 09b218e5 | 2024-09-12 19:21:55 | [diff] [blame] | 230 | } |
Lei Zhang | 3bf0669 | 2020-02-03 19:41:05 | [diff] [blame] | 231 | |
| 232 | *ns_pid_supported = true; |
Helmut Januschka | 91069ddb | 2024-04-09 01:42:16 | [diff] [blame] | 233 | std::vector<std::string_view> split_value_str = SplitStringPiece( |
Lei Zhang | 09b218e5 | 2024-09-12 19:21:55 | [diff] [blame] | 234 | token.value(), "\t", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); |
reveman | e5aece57 | 2017-09-04 07:18:33 | [diff] [blame] | 235 | DCHECK_GE(split_value_str.size(), 2u); |
| 236 | int value; |
| 237 | // The last value in the list is the PID in the namespace. |
| 238 | if (StringToInt(split_value_str.back(), &value) && value == ns_tid) { |
| 239 | // The second value in the list is the real PID. |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 240 | if (StringToInt(split_value_str[1], &value)) { |
reveman | e5aece57 | 2017-09-04 07:18:33 | [diff] [blame] | 241 | return value; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 242 | } |
reveman | 7b97c32 | 2016-09-20 02:10:58 | [diff] [blame] | 243 | } |
reveman | e5aece57 | 2017-09-04 07:18:33 | [diff] [blame] | 244 | break; |
[email protected] | 66218314 | 2010-07-16 19:28:17 | [diff] [blame] | 245 | } |
| 246 | } |
| 247 | return -1; |
| 248 | } |
| 249 | |
[email protected] | 75ae542 | 2009-04-21 17:20:10 | [diff] [blame] | 250 | } // namespace base |