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