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