1 //===-- HostInfoLinux.cpp ---------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "lldb/Core/Log.h" 11 #include "lldb/Host/linux/HostInfoLinux.h" 12 13 #include <limits.h> 14 #include <stdio.h> 15 #include <string.h> 16 #include <sys/utsname.h> 17 18 #include <algorithm> 19 20 using namespace lldb_private; 21 22 namespace 23 { 24 struct HostInfoLinuxFields 25 { 26 HostInfoLinuxFields() 27 : m_os_major(0) 28 , m_os_minor(0) 29 , m_os_update(0) 30 { 31 } 32 33 std::string m_distribution_id; 34 uint32_t m_os_major; 35 uint32_t m_os_minor; 36 uint32_t m_os_update; 37 }; 38 39 HostInfoLinuxFields *g_fields = nullptr; 40 } 41 42 void 43 HostInfoLinux::Initialize() 44 { 45 HostInfoPosix::Initialize(); 46 47 g_fields = new HostInfoLinuxFields(); 48 } 49 50 bool 51 HostInfoLinux::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) 52 { 53 static bool is_initialized = false; 54 static bool success = false; 55 56 if (!is_initialized) 57 { 58 is_initialized = true; 59 struct utsname un; 60 61 if (uname(&un)) 62 goto finished; 63 64 int status = sscanf(un.release, "%u.%u.%u", &g_fields->m_os_major, &g_fields->m_os_minor, &g_fields->m_os_update); 65 if (status == 3) 66 { 67 success = true; 68 goto finished; 69 } 70 71 // Some kernels omit the update version, so try looking for just "X.Y" and 72 // set update to 0. 73 g_fields->m_os_update = 0; 74 status = sscanf(un.release, "%u.%u", &g_fields->m_os_major, &g_fields->m_os_minor); 75 success = !!(status == 2); 76 } 77 78 finished: 79 major = g_fields->m_os_major; 80 minor = g_fields->m_os_minor; 81 update = g_fields->m_os_update; 82 return success; 83 } 84 85 llvm::StringRef 86 HostInfoLinux::GetDistributionId() 87 { 88 static bool is_initialized = false; 89 // Try to run 'lbs_release -i', and use that response 90 // for the distribution id. 91 92 if (!is_initialized) 93 { 94 is_initialized = true; 95 96 Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST)); 97 if (log) 98 log->Printf("attempting to determine Linux distribution..."); 99 100 // check if the lsb_release command exists at one of the 101 // following paths 102 const char *const exe_paths[] = {"/bin/lsb_release", "/usr/bin/lsb_release"}; 103 104 for (size_t exe_index = 0; exe_index < sizeof(exe_paths) / sizeof(exe_paths[0]); ++exe_index) 105 { 106 const char *const get_distribution_info_exe = exe_paths[exe_index]; 107 if (access(get_distribution_info_exe, F_OK)) 108 { 109 // this exe doesn't exist, move on to next exe 110 if (log) 111 log->Printf("executable doesn't exist: %s", get_distribution_info_exe); 112 continue; 113 } 114 115 // execute the distribution-retrieval command, read output 116 std::string get_distribution_id_command(get_distribution_info_exe); 117 get_distribution_id_command += " -i"; 118 119 FILE *file = popen(get_distribution_id_command.c_str(), "r"); 120 if (!file) 121 { 122 if (log) 123 log->Printf("failed to run command: \"%s\", cannot retrieve " 124 "platform information", 125 get_distribution_id_command.c_str()); 126 break; 127 } 128 129 // retrieve the distribution id string. 130 char distribution_id[256] = {'\0'}; 131 if (fgets(distribution_id, sizeof(distribution_id) - 1, file) != NULL) 132 { 133 if (log) 134 log->Printf("distribution id command returned \"%s\"", distribution_id); 135 136 const char *const distributor_id_key = "Distributor ID:\t"; 137 if (strstr(distribution_id, distributor_id_key)) 138 { 139 // strip newlines 140 std::string id_string(distribution_id + strlen(distributor_id_key)); 141 id_string.erase(std::remove(id_string.begin(), id_string.end(), '\n'), id_string.end()); 142 143 // lower case it and convert whitespace to underscores 144 std::transform(id_string.begin(), id_string.end(), id_string.begin(), [](char ch) 145 { 146 return tolower(isspace(ch) ? '_' : ch); 147 }); 148 149 g_fields->m_distribution_id = id_string; 150 if (log) 151 log->Printf("distribution id set to \"%s\"", g_fields->m_distribution_id.c_str()); 152 } 153 else 154 { 155 if (log) 156 log->Printf("failed to find \"%s\" field in \"%s\"", distributor_id_key, distribution_id); 157 } 158 } 159 else 160 { 161 if (log) 162 log->Printf("failed to retrieve distribution id, \"%s\" returned no" 163 " lines", 164 get_distribution_id_command.c_str()); 165 } 166 167 // clean up the file 168 pclose(file); 169 } 170 } 171 172 return g_fields->m_distribution_id.c_str(); 173 } 174 175 FileSpec 176 HostInfoLinux::GetProgramFileSpec() 177 { 178 static FileSpec g_program_filespec; 179 180 if (!g_program_filespec) 181 { 182 char exe_path[PATH_MAX]; 183 ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1); 184 if (len > 0) 185 { 186 exe_path[len] = 0; 187 g_program_filespec.SetFile(exe_path, false); 188 } 189 } 190 191 return g_program_filespec; 192 } 193 194 bool 195 HostInfoLinux::ComputeSystemPluginsDirectory(FileSpec &file_spec) 196 { 197 file_spec.SetFile("/usr/lib/lldb", true); 198 return true; 199 } 200 201 bool 202 HostInfoLinux::ComputeUserPluginsDirectory(FileSpec &file_spec) 203 { 204 // XDG Base Directory Specification 205 // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html 206 // If XDG_DATA_HOME exists, use that, otherwise use ~/.local/share/lldb. 207 FileSpec lldb_file_spec; 208 const char *xdg_data_home = getenv("XDG_DATA_HOME"); 209 if (xdg_data_home && xdg_data_home[0]) 210 { 211 std::string user_plugin_dir(xdg_data_home); 212 user_plugin_dir += "/lldb"; 213 lldb_file_spec.SetFile(user_plugin_dir.c_str(), true); 214 } 215 else 216 lldb_file_spec.SetFile("~/.local/share/lldb", true); 217 218 return true; 219 } 220 221 void 222 HostInfoLinux::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64) 223 { 224 HostInfoPosix::ComputeHostArchitectureSupport(arch_32, arch_64); 225 226 const char *distribution_id = GetDistributionId().data(); 227 228 // On Linux, "unknown" in the vendor slot isn't what we want for the default 229 // triple. It's probably an artifact of config.guess. 230 if (arch_32.IsValid()) 231 { 232 arch_32.SetDistributionId(distribution_id); 233 if (arch_32.GetTriple().getVendor() == llvm::Triple::UnknownVendor) 234 arch_32.GetTriple().setVendorName(""); 235 } 236 if (arch_64.IsValid()) 237 { 238 arch_64.SetDistributionId(distribution_id); 239 if (arch_64.GetTriple().getVendor() == llvm::Triple::UnknownVendor) 240 arch_64.GetTriple().setVendorName(""); 241 } 242 } 243