1 //===-- HostInfoPosix.cpp -------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
28 #include <sys/types.h>
29 #include <sys/utsname.h>
32 using namespace lldb_private
;
35 struct HostInfoPosixFields
{
36 llvm::once_flag m_os_version_once_flag
;
37 llvm::VersionTuple m_os_version
;
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
, []() {
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
);
56 return g_fields
->m_os_version
;
59 size_t HostInfoPosix::GetPageSize() { return ::getpagesize(); }
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) {
71 std::optional
<std::string
> HostInfoPosix::GetOSKernelDescription() {
76 return std::string(un
.version
);
79 std::optional
<std::string
> HostInfoPosix::GetOSBuildString() {
81 ::memset(&un
, 0, sizeof(utsname
));
86 return std::string(un
.release
);
90 #include <android/api-level.h>
92 #if defined(__ANDROID_API__) && __ANDROID_API__ < 21
97 class PosixUserIDResolver
: public UserIDResolver
{
99 std::optional
<std::string
> DoGetUserName(id_t uid
) override
;
100 std::optional
<std::string
> DoGetGroupName(id_t gid
) override
;
105 std::string username
;
109 static std::optional
<PasswdEntry
> GetPassword(id_t uid
) {
111 // getpwuid_r is missing from android-9
112 // The caller should provide some thread safety by making sure no one calls
113 // this function concurrently, because using getpwuid is ultimately not
114 // thread-safe as we don't know who else might be calling it.
115 if (auto *user_info_ptr
= ::getpwuid(uid
))
116 return PasswdEntry
{user_info_ptr
->pw_name
, user_info_ptr
->pw_shell
};
118 struct passwd user_info
;
119 struct passwd
*user_info_ptr
= &user_info
;
120 char user_buffer
[PATH_MAX
];
121 size_t user_buffer_size
= sizeof(user_buffer
);
122 if (::getpwuid_r(uid
, &user_info
, user_buffer
, user_buffer_size
,
123 &user_info_ptr
) == 0 &&
125 return PasswdEntry
{user_info_ptr
->pw_name
, user_info_ptr
->pw_shell
};
131 std::optional
<std::string
> PosixUserIDResolver::DoGetUserName(id_t uid
) {
132 if (std::optional
<PasswdEntry
> password
= GetPassword(uid
))
133 return password
->username
;
137 std::optional
<std::string
> PosixUserIDResolver::DoGetGroupName(id_t gid
) {
139 char group_buffer
[PATH_MAX
];
140 size_t group_buffer_size
= sizeof(group_buffer
);
141 struct group group_info
;
142 struct group
*group_info_ptr
= &group_info
;
143 // Try the threadsafe version first
144 if (::getgrgid_r(gid
, &group_info
, group_buffer
, group_buffer_size
,
145 &group_info_ptr
) == 0) {
147 return std::string(group_info_ptr
->gr_name
);
149 // The threadsafe version isn't currently working for me on darwin, but the
150 // non-threadsafe version is, so I am calling it below.
151 group_info_ptr
= ::getgrgid(gid
);
153 return std::string(group_info_ptr
->gr_name
);
159 static llvm::ManagedStatic
<PosixUserIDResolver
> g_user_id_resolver
;
161 UserIDResolver
&HostInfoPosix::GetUserIDResolver() {
162 return *g_user_id_resolver
;
165 uint32_t HostInfoPosix::GetUserID() { return getuid(); }
167 uint32_t HostInfoPosix::GetGroupID() { return getgid(); }
169 uint32_t HostInfoPosix::GetEffectiveUserID() { return geteuid(); }
171 uint32_t HostInfoPosix::GetEffectiveGroupID() { return getegid(); }
173 FileSpec
HostInfoPosix::GetDefaultShell() {
174 if (const char *v
= ::getenv("SHELL"))
176 if (std::optional
<PasswdEntry
> password
= GetPassword(::geteuid()))
177 return FileSpec(password
->shell
);
178 return FileSpec("/bin/sh");
181 bool HostInfoPosix::ComputeSupportExeDirectory(FileSpec
&file_spec
) {
182 if (ComputePathRelativeToLibrary(file_spec
, "/bin") &&
183 file_spec
.IsAbsolute() && FileSystem::Instance().Exists(file_spec
))
185 file_spec
.SetDirectory(HostInfo::GetProgramFileSpec().GetDirectory());
186 return !file_spec
.GetDirectory().IsEmpty();
189 bool HostInfoPosix::ComputeSystemPluginsDirectory(FileSpec
&file_spec
) {
190 FileSpec
temp_file("/usr/" LLDB_INSTALL_LIBDIR_BASENAME
"/lldb/plugins");
191 FileSystem::Instance().Resolve(temp_file
);
192 file_spec
.SetDirectory(temp_file
.GetPath());
196 bool HostInfoPosix::ComputeUserPluginsDirectory(FileSpec
&file_spec
) {
197 // XDG Base Directory Specification
198 // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html If
199 // XDG_DATA_HOME exists, use that, otherwise use ~/.local/share/lldb.
200 const char *xdg_data_home
= getenv("XDG_DATA_HOME");
201 if (xdg_data_home
&& xdg_data_home
[0]) {
202 std::string
user_plugin_dir(xdg_data_home
);
203 user_plugin_dir
+= "/lldb";
204 file_spec
.SetDirectory(user_plugin_dir
.c_str());
206 file_spec
.SetDirectory("~/.local/share/lldb");
210 bool HostInfoPosix::ComputeHeaderDirectory(FileSpec
&file_spec
) {
211 FileSpec
temp_file("/opt/local/include/lldb");
212 file_spec
.SetDirectory(temp_file
.GetPath());
216 bool HostInfoPosix::GetEnvironmentVar(const std::string
&var_name
,
218 if (const char *pvar
= ::getenv(var_name
.c_str())) {
219 var
= std::string(pvar
);