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