xref: /llvm-project/lldb/source/Host/posix/HostInfoPosix.cpp (revision a6cfde62bb89e595db2bf7bb8ae810293d8edf26)
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 
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 
59 size_t HostInfoPosix::GetPageSize() { return ::getpagesize(); }
60 
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 
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 
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 
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 
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 
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 static llvm::ManagedStatic<PosixUserIDResolver> g_user_id_resolver;
144 
145 UserIDResolver &HostInfoPosix::GetUserIDResolver() {
146   return *g_user_id_resolver;
147 }
148 
149 uint32_t HostInfoPosix::GetUserID() { return getuid(); }
150 
151 uint32_t HostInfoPosix::GetGroupID() { return getgid(); }
152 
153 uint32_t HostInfoPosix::GetEffectiveUserID() { return geteuid(); }
154 
155 uint32_t HostInfoPosix::GetEffectiveGroupID() { return getegid(); }
156 
157 FileSpec HostInfoPosix::GetDefaultShell() {
158   if (const char *v = ::getenv("SHELL"))
159     return FileSpec(v);
160   if (std::optional<PasswdEntry> password = GetPassword(::geteuid()))
161     return FileSpec(password->shell);
162   return FileSpec("/bin/sh");
163 }
164 
165 bool HostInfoPosix::ComputeSupportExeDirectory(FileSpec &file_spec) {
166   if (ComputePathRelativeToLibrary(file_spec, "/bin") &&
167       file_spec.IsAbsolute() && FileSystem::Instance().Exists(file_spec))
168     return true;
169   file_spec.SetDirectory(HostInfo::GetProgramFileSpec().GetDirectory());
170   return !file_spec.GetDirectory().IsEmpty();
171 }
172 
173 bool HostInfoPosix::ComputeSystemPluginsDirectory(FileSpec &file_spec) {
174   FileSpec temp_file("/usr/" LLDB_INSTALL_LIBDIR_BASENAME "/lldb/plugins");
175   FileSystem::Instance().Resolve(temp_file);
176   file_spec.SetDirectory(temp_file.GetPath());
177   return true;
178 }
179 
180 bool HostInfoPosix::ComputeUserPluginsDirectory(FileSpec &file_spec) {
181   // XDG Base Directory Specification
182   // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html If
183   // XDG_DATA_HOME exists, use that, otherwise use ~/.local/share/lldb.
184   const char *xdg_data_home = getenv("XDG_DATA_HOME");
185   if (xdg_data_home && xdg_data_home[0]) {
186     std::string user_plugin_dir(xdg_data_home);
187     user_plugin_dir += "/lldb";
188     file_spec.SetDirectory(user_plugin_dir.c_str());
189   } else
190     file_spec.SetDirectory("~/.local/share/lldb");
191   return true;
192 }
193 
194 bool HostInfoPosix::ComputeHeaderDirectory(FileSpec &file_spec) {
195   FileSpec temp_file("/opt/local/include/lldb");
196   file_spec.SetDirectory(temp_file.GetPath());
197   return true;
198 }
199 
200 bool HostInfoPosix::GetEnvironmentVar(const std::string &var_name,
201                                       std::string &var) {
202   if (const char *pvar = ::getenv(var_name.c_str())) {
203     var = std::string(pvar);
204     return true;
205   }
206   return false;
207 }
208