1 //===-- HostInfoPosix.cpp -------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "lldb/Host/posix/HostInfoPosix.h"
10 #include "lldb/Host/Config.h"
11 #include "lldb/Host/FileSystem.h"
12 #include "lldb/Host/HostInfo.h"
13 #include "lldb/Utility/Log.h"
14 #include "lldb/Utility/UserIDResolver.h"
15 #include "llvm/ADT/SmallString.h"
16 #include "llvm/ADT/Twine.h"
17 #include "llvm/Support/Path.h"
18 #include "llvm/Support/raw_ostream.h"
19
20 #include <climits>
21 #include <cstdio>
22 #include <cstdlib>
23 #include <cstring>
24 #include <grp.h>
25 #include <mutex>
26 #include <optional>
27 #include <pwd.h>
28 #include <sys/types.h>
29 #include <sys/utsname.h>
30 #include <unistd.h>
31
32 using namespace lldb_private;
33
34 namespace {
35 struct HostInfoPosixFields {
36 llvm::once_flag m_os_version_once_flag;
37 llvm::VersionTuple m_os_version;
38 };
39 } // namespace
40
GetOSVersion()41 llvm::VersionTuple HostInfoPosix::GetOSVersion() {
42 static HostInfoPosixFields *g_fields = new HostInfoPosixFields();
43 assert(g_fields && "Missing call to Initialize?");
44 llvm::call_once(g_fields->m_os_version_once_flag, []() {
45 struct utsname un;
46 if (uname(&un) != 0)
47 return;
48
49 llvm::StringRef release = un.release;
50 // The Linux kernel release string can include a lot of stuff (e.g.
51 // 4.9.0-6-amd64). We're only interested in the numbered prefix.
52 release = release.substr(0, release.find_first_not_of("0123456789."));
53 g_fields->m_os_version.tryParse(release);
54 });
55
56 return g_fields->m_os_version;
57 }
58
GetPageSize()59 size_t HostInfoPosix::GetPageSize() { return ::getpagesize(); }
60
GetHostname(std::string & s)61 bool HostInfoPosix::GetHostname(std::string &s) {
62 char hostname[PATH_MAX];
63 hostname[sizeof(hostname) - 1] = '\0';
64 if (::gethostname(hostname, sizeof(hostname) - 1) == 0) {
65 s.assign(hostname);
66 return true;
67 }
68 return false;
69 }
70
GetOSKernelDescription()71 std::optional<std::string> HostInfoPosix::GetOSKernelDescription() {
72 struct utsname un;
73 if (uname(&un) < 0)
74 return std::nullopt;
75
76 return std::string(un.version);
77 }
78
GetOSBuildString()79 std::optional<std::string> HostInfoPosix::GetOSBuildString() {
80 struct utsname un;
81 ::memset(&un, 0, sizeof(utsname));
82
83 if (uname(&un) < 0)
84 return std::nullopt;
85
86 return std::string(un.release);
87 }
88
89 namespace {
90 class PosixUserIDResolver : public UserIDResolver {
91 protected:
92 std::optional<std::string> DoGetUserName(id_t uid) override;
93 std::optional<std::string> DoGetGroupName(id_t gid) override;
94 };
95 } // namespace
96
97 struct PasswdEntry {
98 std::string username;
99 std::string shell;
100 };
101
GetPassword(id_t uid)102 static std::optional<PasswdEntry> GetPassword(id_t uid) {
103 struct passwd user_info;
104 struct passwd *user_info_ptr = &user_info;
105 char user_buffer[PATH_MAX];
106 size_t user_buffer_size = sizeof(user_buffer);
107 if (::getpwuid_r(uid, &user_info, user_buffer, user_buffer_size,
108 &user_info_ptr) == 0 &&
109 user_info_ptr) {
110 return PasswdEntry{user_info_ptr->pw_name, user_info_ptr->pw_shell};
111 }
112 return std::nullopt;
113 }
114
DoGetUserName(id_t uid)115 std::optional<std::string> PosixUserIDResolver::DoGetUserName(id_t uid) {
116 if (std::optional<PasswdEntry> password = GetPassword(uid))
117 return password->username;
118 return std::nullopt;
119 }
120
DoGetGroupName(id_t gid)121 std::optional<std::string> PosixUserIDResolver::DoGetGroupName(id_t gid) {
122 #if !defined(__ANDROID__) || __ANDROID_API__ >= 24
123 char group_buffer[PATH_MAX];
124 size_t group_buffer_size = sizeof(group_buffer);
125 struct group group_info;
126 struct group *group_info_ptr = &group_info;
127 // Try the threadsafe version first
128 if (::getgrgid_r(gid, &group_info, group_buffer, group_buffer_size,
129 &group_info_ptr) == 0) {
130 if (group_info_ptr)
131 return std::string(group_info_ptr->gr_name);
132 } else {
133 // The threadsafe version isn't currently working for me on darwin, but the
134 // non-threadsafe version is, so I am calling it below.
135 group_info_ptr = ::getgrgid(gid);
136 if (group_info_ptr)
137 return std::string(group_info_ptr->gr_name);
138 }
139 #endif
140 return std::nullopt;
141 }
142
143 /// The SDK is the directory where the system C headers, libraries, can be
144 /// found. On POSIX platforms this is simply the root directory.
GetSDKRoot(SDKOptions options)145 llvm::Expected<llvm::StringRef> HostInfoPosix::GetSDKRoot(SDKOptions options) {
146 return "/";
147 }
148
149 static llvm::ManagedStatic<PosixUserIDResolver> g_user_id_resolver;
150
GetUserIDResolver()151 UserIDResolver &HostInfoPosix::GetUserIDResolver() {
152 return *g_user_id_resolver;
153 }
154
GetUserID()155 uint32_t HostInfoPosix::GetUserID() { return getuid(); }
156
GetGroupID()157 uint32_t HostInfoPosix::GetGroupID() { return getgid(); }
158
GetEffectiveUserID()159 uint32_t HostInfoPosix::GetEffectiveUserID() { return geteuid(); }
160
GetEffectiveGroupID()161 uint32_t HostInfoPosix::GetEffectiveGroupID() { return getegid(); }
162
GetDefaultShell()163 FileSpec HostInfoPosix::GetDefaultShell() {
164 if (const char *v = ::getenv("SHELL"))
165 return FileSpec(v);
166 if (std::optional<PasswdEntry> password = GetPassword(::geteuid()))
167 return FileSpec(password->shell);
168 return FileSpec("/bin/sh");
169 }
170
ComputeSupportExeDirectory(FileSpec & file_spec)171 bool HostInfoPosix::ComputeSupportExeDirectory(FileSpec &file_spec) {
172 if (ComputePathRelativeToLibrary(file_spec, "/bin") &&
173 file_spec.IsAbsolute() && FileSystem::Instance().Exists(file_spec))
174 return true;
175 file_spec.SetDirectory(HostInfo::GetProgramFileSpec().GetDirectory());
176 return !file_spec.GetDirectory().IsEmpty();
177 }
178
ComputeSystemPluginsDirectory(FileSpec & file_spec)179 bool HostInfoPosix::ComputeSystemPluginsDirectory(FileSpec &file_spec) {
180 FileSpec temp_file("/usr/" LLDB_INSTALL_LIBDIR_BASENAME "/lldb/plugins");
181 FileSystem::Instance().Resolve(temp_file);
182 file_spec.SetDirectory(temp_file.GetPath());
183 return true;
184 }
185
ComputeUserPluginsDirectory(FileSpec & file_spec)186 bool HostInfoPosix::ComputeUserPluginsDirectory(FileSpec &file_spec) {
187 // XDG Base Directory Specification
188 // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html If
189 // XDG_DATA_HOME exists, use that, otherwise use ~/.local/share/lldb.
190 const char *xdg_data_home = getenv("XDG_DATA_HOME");
191 if (xdg_data_home && xdg_data_home[0]) {
192 std::string user_plugin_dir(xdg_data_home);
193 user_plugin_dir += "/lldb";
194 file_spec.SetDirectory(user_plugin_dir.c_str());
195 } else
196 file_spec.SetDirectory("~/.local/share/lldb");
197 return true;
198 }
199
ComputeHeaderDirectory(FileSpec & file_spec)200 bool HostInfoPosix::ComputeHeaderDirectory(FileSpec &file_spec) {
201 FileSpec temp_file("/opt/local/include/lldb");
202 file_spec.SetDirectory(temp_file.GetPath());
203 return true;
204 }
205
GetEnvironmentVar(const std::string & var_name,std::string & var)206 bool HostInfoPosix::GetEnvironmentVar(const std::string &var_name,
207 std::string &var) {
208 if (const char *pvar = ::getenv(var_name.c_str())) {
209 var = std::string(pvar);
210 return true;
211 }
212 return false;
213 }
214