[M73 Dev][Tizen] Fix compilation errors for TV profile
[platform/framework/web/chromium-efl.git] / base / linux_util.cc
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
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"
30
31 namespace {
32
33 // Not needed for OS_CHROMEOS.
34 #if defined(OS_LINUX)
35 enum LinuxDistroState {
36   STATE_DID_NOT_CHECK  = 0,
37   STATE_CHECK_STARTED  = 1,
38   STATE_CHECK_FINISHED = 2,
39 };
40
41 // Helper class for GetLinuxDistro().
42 class LinuxDistroHelper {
43  public:
44   // Retrieves the Singleton.
45   static LinuxDistroHelper* GetInstance() {
46     return base::Singleton<LinuxDistroHelper>::get();
47   }
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) {}
52   ~LinuxDistroHelper() = default;
53
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() {
58     base::AutoLock scoped_lock(lock_);
59     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() {
68     base::AutoLock scoped_lock(lock_);
69     DCHECK_EQ(STATE_CHECK_STARTED, state_);
70     state_ = STATE_CHECK_FINISHED;
71   }
72
73  private:
74   base::Lock lock_;
75   LinuxDistroState state_;
76 };
77 #endif  // if defined(OS_LINUX)
78
79 }  // namespace
80
81 namespace base {
82
83 // Account for the terminating null character.
84 static const int kDistroSize = 128 + 1;
85
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)
90     "CrOS";
91 #elif defined(OS_ANDROID)
92     "Android";
93 #else  // if defined(OS_LINUX)
94     "Unknown";
95 #endif
96
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");
114   std::string output;
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)));
121     }
122   }
123   distro_state_singleton->CheckFinished();
124   return g_linux_distro;
125 #else
126   NOTIMPLEMENTED();
127   return "Unknown";
128 #endif
129 }
130
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
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   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
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;
163
164   std::vector<pid_t> tids;
165   if (!GetThreadsForProcess(pid, &tids))
166     return -1;
167
168   std::unique_ptr<char[]> syscall_data(new char[expected_data.length()]);
169   for (pid_t tid : tids) {
170     char buf[256];
171     snprintf(buf, sizeof(buf), "/proc/%d/task/%d/syscall", pid, tid);
172     int fd = open(buf, O_RDONLY);
173     if (fd < 0)
174       continue;
175     if (syscall_supported != nullptr)
176       *syscall_supported = true;
177     bool read_ret = ReadFromFD(fd, syscall_data.get(), expected_data.length());
178     close(fd);
179     if (!read_ret)
180       continue;
181
182     if (0 == strncmp(expected_data.c_str(), syscall_data.get(),
183                      expected_data.length())) {
184       return tid;
185     }
186   }
187   return -1;
188 }
189
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;
193
194   std::vector<pid_t> tids;
195   if (!GetThreadsForProcess(pid, &tids))
196     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;
204     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;
220       }
221       break;
222     }
223   }
224   return -1;
225 }
226
227 }  // namespace base