• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
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 <memory>
17 #include <vector>
18 
19 #include "base/command_line.h"
20 #include "base/files/file_util.h"
21 #include "base/memory/singleton.h"
22 #include "base/process/launch.h"
23 #include "base/strings/string_number_conversions.h"
24 #include "base/strings/string_split.h"
25 #include "base/strings/string_tokenizer.h"
26 #include "base/strings/string_util.h"
27 #include "base/synchronization/lock.h"
28 #include "build/build_config.h"
29 
30 namespace {
31 
32 // Not needed for OS_CHROMEOS.
33 #if defined(OS_LINUX)
34 enum LinuxDistroState {
35   STATE_DID_NOT_CHECK  = 0,
36   STATE_CHECK_STARTED  = 1,
37   STATE_CHECK_FINISHED = 2,
38 };
39 
40 // Helper class for GetLinuxDistro().
41 class LinuxDistroHelper {
42  public:
43   // Retrieves the Singleton.
GetInstance()44   static LinuxDistroHelper* GetInstance() {
45     return base::Singleton<LinuxDistroHelper>::get();
46   }
47 
48   // The simple state machine goes from:
49   // STATE_DID_NOT_CHECK -> STATE_CHECK_STARTED -> STATE_CHECK_FINISHED.
LinuxDistroHelper()50   LinuxDistroHelper() : state_(STATE_DID_NOT_CHECK) {}
51   ~LinuxDistroHelper() = default;
52 
53   // Retrieve the current state, if we're in STATE_DID_NOT_CHECK,
54   // we automatically move to STATE_CHECK_STARTED so nobody else will
55   // do the check.
State()56   LinuxDistroState State() {
57     base::AutoLock scoped_lock(lock_);
58     if (STATE_DID_NOT_CHECK == state_) {
59       state_ = STATE_CHECK_STARTED;
60       return STATE_DID_NOT_CHECK;
61     }
62     return state_;
63   }
64 
65   // Indicate the check finished, move to STATE_CHECK_FINISHED.
CheckFinished()66   void CheckFinished() {
67     base::AutoLock scoped_lock(lock_);
68     DCHECK_EQ(STATE_CHECK_STARTED, state_);
69     state_ = STATE_CHECK_FINISHED;
70   }
71 
72  private:
73   base::Lock lock_;
74   LinuxDistroState state_;
75 };
76 #endif  // if defined(OS_LINUX)
77 
GetTasksForProcess(pid_t pid,std::vector<pid_t> * tids)78 bool GetTasksForProcess(pid_t pid, std::vector<pid_t>* tids) {
79   char buf[256];
80   snprintf(buf, sizeof(buf), "/proc/%d/task", pid);
81 
82   DIR* task = opendir(buf);
83   if (!task) {
84     DLOG(WARNING) << "Cannot open " << buf;
85     return false;
86   }
87 
88   struct dirent* dent;
89   while ((dent = readdir(task))) {
90     char* endptr;
91     const unsigned long int tid_ul = strtoul(dent->d_name, &endptr, 10);
92     if (tid_ul == ULONG_MAX || *endptr)
93       continue;
94     tids->push_back(tid_ul);
95   }
96   closedir(task);
97   return true;
98 }
99 
100 }  // namespace
101 
102 namespace base {
103 
104 // Account for the terminating null character.
105 static const int kDistroSize = 128 + 1;
106 
107 // We use this static string to hold the Linux distro info. If we
108 // crash, the crash handler code will send this in the crash dump.
109 char g_linux_distro[kDistroSize] =
110 #if defined(OS_CHROMEOS)
111     "CrOS";
112 #elif defined(OS_ANDROID)
113     "Android";
114 #else  // if defined(OS_LINUX)
115     "Unknown";
116 #endif
117 
GetLinuxDistro()118 std::string GetLinuxDistro() {
119 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
120   return g_linux_distro;
121 #elif defined(OS_LINUX)
122   LinuxDistroHelper* distro_state_singleton = LinuxDistroHelper::GetInstance();
123   LinuxDistroState state = distro_state_singleton->State();
124   if (STATE_CHECK_FINISHED == state)
125     return g_linux_distro;
126   if (STATE_CHECK_STARTED == state)
127     return "Unknown"; // Don't wait for other thread to finish.
128   DCHECK_EQ(state, STATE_DID_NOT_CHECK);
129   // We do this check only once per process. If it fails, there's
130   // little reason to believe it will work if we attempt to run
131   // lsb_release again.
132   std::vector<std::string> argv;
133   argv.push_back("lsb_release");
134   argv.push_back("-d");
135   std::string output;
136   GetAppOutput(CommandLine(argv), &output);
137   if (output.length() > 0) {
138     // lsb_release -d should return: Description:<tab>Distro Info
139     const char field[] = "Description:\t";
140     if (output.compare(0, strlen(field), field) == 0) {
141       SetLinuxDistro(output.substr(strlen(field)));
142     }
143   }
144   distro_state_singleton->CheckFinished();
145   return g_linux_distro;
146 #else
147   NOTIMPLEMENTED();
148   return "Unknown";
149 #endif
150 }
151 
SetLinuxDistro(const std::string & distro)152 void SetLinuxDistro(const std::string& distro) {
153   std::string trimmed_distro;
154   TrimWhitespaceASCII(distro, TRIM_ALL, &trimmed_distro);
155   strlcpy(g_linux_distro, trimmed_distro.c_str(), kDistroSize);
156 }
157 
FindThreadIDWithSyscall(pid_t pid,const std::string & expected_data,bool * syscall_supported)158 pid_t FindThreadIDWithSyscall(pid_t pid, const std::string& expected_data,
159                               bool* syscall_supported) {
160   if (syscall_supported != nullptr)
161     *syscall_supported = false;
162 
163   std::vector<pid_t> tids;
164   if (!GetTasksForProcess(pid, &tids))
165     return -1;
166 
167   std::unique_ptr<char[]> syscall_data(new char[expected_data.length()]);
168   for (pid_t tid : tids) {
169     char buf[256];
170     snprintf(buf, sizeof(buf), "/proc/%d/task/%d/syscall", pid, tid);
171     int fd = open(buf, O_RDONLY);
172     if (fd < 0)
173       continue;
174     if (syscall_supported != nullptr)
175       *syscall_supported = true;
176     bool read_ret = ReadFromFD(fd, syscall_data.get(), expected_data.length());
177     close(fd);
178     if (!read_ret)
179       continue;
180 
181     if (0 == strncmp(expected_data.c_str(), syscall_data.get(),
182                      expected_data.length())) {
183       return tid;
184     }
185   }
186   return -1;
187 }
188 
FindThreadID(pid_t pid,pid_t ns_tid,bool * ns_pid_supported)189 pid_t FindThreadID(pid_t pid, pid_t ns_tid, bool* ns_pid_supported) {
190   if (ns_pid_supported)
191     *ns_pid_supported = false;
192 
193   std::vector<pid_t> tids;
194   if (!GetTasksForProcess(pid, &tids))
195     return -1;
196 
197   for (pid_t tid : tids) {
198     char buf[256];
199     snprintf(buf, sizeof(buf), "/proc/%d/task/%d/status", pid, tid);
200     std::string status;
201     if (!ReadFileToString(FilePath(buf), &status))
202       return -1;
203     StringTokenizer tokenizer(status, "\n");
204     while (tokenizer.GetNext()) {
205       StringPiece value_str(tokenizer.token_piece());
206       if (!value_str.starts_with("NSpid"))
207         continue;
208       if (ns_pid_supported)
209         *ns_pid_supported = true;
210       std::vector<StringPiece> split_value_str = SplitStringPiece(
211           value_str, "\t", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
212       DCHECK_GE(split_value_str.size(), 2u);
213       int value;
214       // The last value in the list is the PID in the namespace.
215       if (StringToInt(split_value_str.back(), &value) && value == ns_tid) {
216         // The second value in the list is the real PID.
217         if (StringToInt(split_value_str[1], &value))
218           return value;
219       }
220       break;
221     }
222   }
223   return -1;
224 }
225 
226 }  // namespace base
227