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