blob: caf471a39e15c207c2a2e85294777119abbfefd7 [file] [log] [blame]
[email protected]46e9acad2012-06-13 23:20:041// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]75ae5422009-04-21 17:20:102// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]912c6452009-07-17 05:55:515#include "base/linux_util.h"
[email protected]75ae5422009-04-21 17:20:106
[email protected]85ebe8f2009-10-29 04:02:557#include <dirent.h>
8#include <errno.h>
[email protected]662183142010-07-16 19:28:179#include <fcntl.h>
avi9b6f42932015-12-26 22:15:1410#include <limits.h>
[email protected]75ae5422009-04-21 17:20:1011#include <stdlib.h>
[email protected]85ebe8f2009-10-29 04:02:5512#include <sys/stat.h>
[email protected]662183142010-07-16 19:28:1713#include <sys/types.h>
[email protected]85ebe8f2009-10-29 04:02:5514#include <unistd.h>
[email protected]75ae5422009-04-21 17:20:1015
dcheng093de9b2016-04-04 21:25:5116#include <memory>
[email protected]912c6452009-07-17 05:55:5117
18#include "base/command_line.h"
Alexandr Ilind4f4b342019-01-08 15:34:0919#include "base/files/dir_reader_posix.h"
[email protected]e3177dd52014-08-13 20:22:1420#include "base/files/file_util.h"
[email protected]3b63f8f42011-03-28 01:54:1521#include "base/memory/singleton.h"
[email protected]dd4b51262013-07-25 21:38:2322#include "base/process/launch.h"
Alexandr Ilind4f4b342019-01-08 15:34:0923#include "base/strings/safe_sprintf.h"
reveman7b97c322016-09-20 02:10:5824#include "base/strings/string_number_conversions.h"
25#include "base/strings/string_split.h"
revemane5aece572017-09-04 07:18:3326#include "base/strings/string_tokenizer.h"
[email protected]d529cb02013-06-10 19:06:5727#include "base/strings/string_util.h"
[email protected]20305ec2011-01-21 04:55:5228#include "base/synchronization/lock.h"
avi9b6f42932015-12-26 22:15:1429#include "build/build_config.h"
[email protected]87fc1682009-07-22 00:22:4930
31namespace {
32
[email protected]78f6f512009-10-21 19:04:1733// Not needed for OS_CHROMEOS.
34#if defined(OS_LINUX)
[email protected]19467c02009-10-13 02:51:0735enum LinuxDistroState {
36 STATE_DID_NOT_CHECK = 0,
37 STATE_CHECK_STARTED = 1,
38 STATE_CHECK_FINISHED = 2,
39};
40
41// Helper class for GetLinuxDistro().
42class LinuxDistroHelper {
43 public:
44 // Retrieves the Singleton.
[email protected]dc8caba2010-12-13 16:52:3545 static LinuxDistroHelper* GetInstance() {
olli.raula36aa8be2015-09-10 11:14:2246 return base::Singleton<LinuxDistroHelper>::get();
[email protected]19467c02009-10-13 02:51:0747 }
48
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) {}
Chris Watkinsbb7211c2017-11-29 07:16:3852 ~LinuxDistroHelper() = default;
[email protected]19467c02009-10-13 02:51:0753
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
56 // do the check.
57 LinuxDistroState State() {
[email protected]20305ec2011-01-21 04:55:5258 base::AutoLock scoped_lock(lock_);
[email protected]19467c02009-10-13 02:51:0759 if (STATE_DID_NOT_CHECK == state_) {
60 state_ = STATE_CHECK_STARTED;
61 return STATE_DID_NOT_CHECK;
62 }
63 return state_;
64 }
65
66 // Indicate the check finished, move to STATE_CHECK_FINISHED.
67 void CheckFinished() {
[email protected]20305ec2011-01-21 04:55:5268 base::AutoLock scoped_lock(lock_);
[email protected]60ea6052011-04-18 20:07:0869 DCHECK_EQ(STATE_CHECK_STARTED, state_);
[email protected]19467c02009-10-13 02:51:0770 state_ = STATE_CHECK_FINISHED;
71 }
72
73 private:
[email protected]20305ec2011-01-21 04:55:5274 base::Lock lock_;
[email protected]19467c02009-10-13 02:51:0775 LinuxDistroState state_;
76};
[email protected]78f6f512009-10-21 19:04:1777#endif // if defined(OS_LINUX)
[email protected]19467c02009-10-13 02:51:0778
[email protected]9bc8cff2010-04-03 01:05:3979} // namespace
[email protected]912c6452009-07-17 05:55:5180
[email protected]75ae5422009-04-21 17:20:1081namespace base {
82
[email protected]6dde9d72010-08-26 08:55:2283// Account for the terminating null character.
84static const int kDistroSize = 128 + 1;
85
[email protected]912c6452009-07-17 05:55:5186// 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.
[email protected]6dde9d72010-08-26 08:55:2288char g_linux_distro[kDistroSize] =
[email protected]cbf0d1d2012-08-15 20:54:0689#if defined(OS_CHROMEOS)
[email protected]78f6f512009-10-21 19:04:1790 "CrOS";
[email protected]84a1ab5a2012-07-18 01:49:2291#elif defined(OS_ANDROID)
92 "Android";
[email protected]78f6f512009-10-21 19:04:1793#else // if defined(OS_LINUX)
94 "Unknown";
95#endif
[email protected]912c6452009-07-17 05:55:5196
97std::string GetLinuxDistro() {
[email protected]84a1ab5a2012-07-18 01:49:2298#if defined(OS_CHROMEOS) || defined(OS_ANDROID)
[email protected]6dde9d72010-08-26 08:55:2299 return g_linux_distro;
[email protected]66700d42010-03-10 07:46:43100#elif defined(OS_LINUX)
[email protected]dc8caba2010-12-13 16:52:35101 LinuxDistroHelper* distro_state_singleton = LinuxDistroHelper::GetInstance();
[email protected]19467c02009-10-13 02:51:07102 LinuxDistroState state = distro_state_singleton->State();
[email protected]84a1ab5a2012-07-18 01:49:22103 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");
114 std::string output;
olli.raula36aa8be2015-09-10 11:14:22115 GetAppOutput(CommandLine(argv), &output);
[email protected]84a1ab5a2012-07-18 01:49:22116 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)));
[email protected]912c6452009-07-17 05:55:51121 }
[email protected]912c6452009-07-17 05:55:51122 }
[email protected]84a1ab5a2012-07-18 01:49:22123 distro_state_singleton->CheckFinished();
124 return g_linux_distro;
[email protected]66700d42010-03-10 07:46:43125#else
126 NOTIMPLEMENTED();
[email protected]167ec822011-10-24 22:05:27127 return "Unknown";
[email protected]78f6f512009-10-21 19:04:17128#endif
[email protected]912c6452009-07-17 05:55:51129}
130
[email protected]6dde9d72010-08-26 08:55:22131void SetLinuxDistro(const std::string& distro) {
132 std::string trimmed_distro;
olli.raula36aa8be2015-09-10 11:14:22133 TrimWhitespaceASCII(distro, TRIM_ALL, &trimmed_distro);
134 strlcpy(g_linux_distro, trimmed_distro.c_str(), kDistroSize);
[email protected]6dde9d72010-08-26 08:55:22135}
136
Alexandr Ilind4f4b342019-01-08 15:34:09137bool 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 base::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 char* endptr;
150 const unsigned long int tid_ul = strtoul(dir_reader.name(), &endptr, 10);
151 if (tid_ul == ULONG_MAX || *endptr)
152 continue;
153 tids->push_back(tid_ul);
154 }
155
156 return true;
157}
158
[email protected]cb7d53e2011-06-21 04:21:06159pid_t FindThreadIDWithSyscall(pid_t pid, const std::string& expected_data,
160 bool* syscall_supported) {
Ivan Kotenkova16212a52017-11-08 12:37:33161 if (syscall_supported != nullptr)
[email protected]cb7d53e2011-06-21 04:21:06162 *syscall_supported = false;
163
[email protected]662183142010-07-16 19:28:17164 std::vector<pid_t> tids;
Alexandr Ilind4f4b342019-01-08 15:34:09165 if (!GetThreadsForProcess(pid, &tids))
reveman7b97c322016-09-20 02:10:58166 return -1;
[email protected]662183142010-07-16 19:28:17167
dcheng093de9b2016-04-04 21:25:51168 std::unique_ptr<char[]> syscall_data(new char[expected_data.length()]);
reveman7b97c322016-09-20 02:10:58169 for (pid_t tid : tids) {
170 char buf[256];
171 snprintf(buf, sizeof(buf), "/proc/%d/task/%d/syscall", pid, tid);
[email protected]662183142010-07-16 19:28:17172 int fd = open(buf, O_RDONLY);
173 if (fd < 0)
174 continue;
Ivan Kotenkova16212a52017-11-08 12:37:33175 if (syscall_supported != nullptr)
[email protected]cb7d53e2011-06-21 04:21:06176 *syscall_supported = true;
[email protected]b264eab2013-11-27 23:22:08177 bool read_ret = ReadFromFD(fd, syscall_data.get(), expected_data.length());
[email protected]662183142010-07-16 19:28:17178 close(fd);
179 if (!read_ret)
180 continue;
181
182 if (0 == strncmp(expected_data.c_str(), syscall_data.get(),
183 expected_data.length())) {
reveman7b97c322016-09-20 02:10:58184 return tid;
185 }
186 }
187 return -1;
188}
189
190pid_t FindThreadID(pid_t pid, pid_t ns_tid, bool* ns_pid_supported) {
191 if (ns_pid_supported)
192 *ns_pid_supported = false;
193
194 std::vector<pid_t> tids;
Alexandr Ilind4f4b342019-01-08 15:34:09195 if (!GetThreadsForProcess(pid, &tids))
reveman7b97c322016-09-20 02:10:58196 return -1;
197
198 for (pid_t tid : tids) {
199 char buf[256];
200 snprintf(buf, sizeof(buf), "/proc/%d/task/%d/status", pid, tid);
201 std::string status;
202 if (!ReadFileToString(FilePath(buf), &status))
203 return -1;
revemane5aece572017-09-04 07:18:33204 StringTokenizer tokenizer(status, "\n");
205 while (tokenizer.GetNext()) {
206 StringPiece value_str(tokenizer.token_piece());
207 if (!value_str.starts_with("NSpid"))
208 continue;
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);
214 int value;
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))
219 return value;
reveman7b97c322016-09-20 02:10:58220 }
revemane5aece572017-09-04 07:18:33221 break;
[email protected]662183142010-07-16 19:28:17222 }
223 }
224 return -1;
225}
226
[email protected]75ae5422009-04-21 17:20:10227} // namespace base