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.
5 #include "base/linux_util.h"
13 #include <sys/types.h>
18 #include "base/command_line.h"
19 #include "base/files/dir_reader_posix.h"
20 #include "base/files/file_util.h"
21 #include "base/memory/singleton.h"
22 #include "base/process/launch.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 "base/synchronization/lock.h"
29 #include "build/build_config.h"
33 // Not needed for OS_CHROMEOS.
35 enum LinuxDistroState {
36 STATE_DID_NOT_CHECK = 0,
37 STATE_CHECK_STARTED = 1,
38 STATE_CHECK_FINISHED = 2,
41 // Helper class for GetLinuxDistro().
42 class LinuxDistroHelper {
44 // Retrieves the Singleton.
45 static LinuxDistroHelper* GetInstance() {
46 return base::Singleton<LinuxDistroHelper>::get();
49 // The simple state machine goes from:
50 // STATE_DID_NOT_CHECK -> STATE_CHECK_STARTED -> STATE_CHECK_FINISHED.
51 LinuxDistroHelper() : state_(STATE_DID_NOT_CHECK) {}
52 ~LinuxDistroHelper() = default;
54 // Retrieve the current state, if we're in STATE_DID_NOT_CHECK,
55 // we automatically move to STATE_CHECK_STARTED so nobody else will
57 LinuxDistroState State() {
58 base::AutoLock scoped_lock(lock_);
59 if (STATE_DID_NOT_CHECK == state_) {
60 state_ = STATE_CHECK_STARTED;
61 return STATE_DID_NOT_CHECK;
66 // Indicate the check finished, move to STATE_CHECK_FINISHED.
67 void CheckFinished() {
68 base::AutoLock scoped_lock(lock_);
69 DCHECK_EQ(STATE_CHECK_STARTED, state_);
70 state_ = STATE_CHECK_FINISHED;
75 LinuxDistroState state_;
77 #endif // if defined(OS_LINUX)
83 // Account for the terminating null character.
84 static const int kDistroSize = 128 + 1;
86 // We use this static string to hold the Linux distro info. If we
87 // crash, the crash handler code will send this in the crash dump.
88 char g_linux_distro[kDistroSize] =
89 #if defined(OS_CHROMEOS)
91 #elif defined(OS_ANDROID)
93 #else // if defined(OS_LINUX)
97 std::string GetLinuxDistro() {
98 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
99 return g_linux_distro;
100 #elif defined(OS_LINUX)
101 LinuxDistroHelper* distro_state_singleton = LinuxDistroHelper::GetInstance();
102 LinuxDistroState state = distro_state_singleton->State();
103 if (STATE_CHECK_FINISHED == state)
104 return g_linux_distro;
105 if (STATE_CHECK_STARTED == state)
106 return "Unknown"; // Don't wait for other thread to finish.
107 DCHECK_EQ(state, STATE_DID_NOT_CHECK);
108 // We do this check only once per process. If it fails, there's
109 // little reason to believe it will work if we attempt to run
110 // lsb_release again.
111 std::vector<std::string> argv;
112 argv.push_back("lsb_release");
113 argv.push_back("-d");
115 GetAppOutput(CommandLine(argv), &output);
116 if (output.length() > 0) {
117 // lsb_release -d should return: Description:<tab>Distro Info
118 const char field[] = "Description:\t";
119 if (output.compare(0, strlen(field), field) == 0) {
120 SetLinuxDistro(output.substr(strlen(field)));
123 distro_state_singleton->CheckFinished();
124 return g_linux_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);
137 bool GetThreadsForProcess(pid_t pid, std::vector<pid_t>* tids) {
138 // 25 > strlen("/proc//task") + strlen(std::to_string(INT_MAX)) + 1 = 22
140 base::strings::SafeSPrintf(buf, "/proc/%d/task", pid);
141 DirReaderPosix dir_reader(buf);
143 if (!dir_reader.IsValid()) {
144 DLOG(WARNING) << "Cannot open " << buf;
148 while (dir_reader.Next()) {
150 const unsigned long int tid_ul = strtoul(dir_reader.name(), &endptr, 10);
151 if (tid_ul == ULONG_MAX || *endptr)
153 tids->push_back(tid_ul);
159 pid_t FindThreadIDWithSyscall(pid_t pid, const std::string& expected_data,
160 bool* syscall_supported) {
161 if (syscall_supported != nullptr)
162 *syscall_supported = false;
164 std::vector<pid_t> tids;
165 if (!GetThreadsForProcess(pid, &tids))
168 std::unique_ptr<char[]> syscall_data(new char[expected_data.length()]);
169 for (pid_t tid : tids) {
171 snprintf(buf, sizeof(buf), "/proc/%d/task/%d/syscall", pid, tid);
172 int fd = open(buf, O_RDONLY);
175 if (syscall_supported != nullptr)
176 *syscall_supported = true;
177 bool read_ret = ReadFromFD(fd, syscall_data.get(), expected_data.length());
182 if (0 == strncmp(expected_data.c_str(), syscall_data.get(),
183 expected_data.length())) {
190 pid_t FindThreadID(pid_t pid, pid_t ns_tid, bool* ns_pid_supported) {
191 if (ns_pid_supported)
192 *ns_pid_supported = false;
194 std::vector<pid_t> tids;
195 if (!GetThreadsForProcess(pid, &tids))
198 for (pid_t tid : tids) {
200 snprintf(buf, sizeof(buf), "/proc/%d/task/%d/status", pid, tid);
202 if (!ReadFileToString(FilePath(buf), &status))
204 StringTokenizer tokenizer(status, "\n");
205 while (tokenizer.GetNext()) {
206 StringPiece value_str(tokenizer.token_piece());
207 if (!value_str.starts_with("NSpid"))
209 if (ns_pid_supported)
210 *ns_pid_supported = true;
211 std::vector<StringPiece> split_value_str = SplitStringPiece(
212 value_str, "\t", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
213 DCHECK_GE(split_value_str.size(), 2u);
215 // The last value in the list is the PID in the namespace.
216 if (StringToInt(split_value_str.back(), &value) && value == ns_tid) {
217 // The second value in the list is the real PID.
218 if (StringToInt(split_value_str[1], &value))