197a14e60SZachary Turner //===-- HostInfoLinux.cpp ---------------------------------------*- C++ -*-===//
297a14e60SZachary Turner //
397a14e60SZachary Turner //                     The LLVM Compiler Infrastructure
497a14e60SZachary Turner //
597a14e60SZachary Turner // This file is distributed under the University of Illinois Open Source
697a14e60SZachary Turner // License. See LICENSE.TXT for details.
797a14e60SZachary Turner //
897a14e60SZachary Turner //===----------------------------------------------------------------------===//
997a14e60SZachary Turner 
1097a14e60SZachary Turner #include "lldb/Core/Log.h"
1197a14e60SZachary Turner #include "lldb/Host/linux/HostInfoLinux.h"
1297a14e60SZachary Turner 
13e47ffc3bSZachary Turner #include <limits.h>
1497a14e60SZachary Turner #include <stdio.h>
1597a14e60SZachary Turner #include <string.h>
1697a14e60SZachary Turner #include <sys/utsname.h>
1797a14e60SZachary Turner 
1897a14e60SZachary Turner #include <algorithm>
19*ff48e4beSGreg Clayton #include <mutex> // std::once
2097a14e60SZachary Turner 
2197a14e60SZachary Turner using namespace lldb_private;
2297a14e60SZachary Turner 
23673b6e4fSZachary Turner namespace
24673b6e4fSZachary Turner {
25673b6e4fSZachary Turner struct HostInfoLinuxFields
26673b6e4fSZachary Turner {
27673b6e4fSZachary Turner     HostInfoLinuxFields()
28673b6e4fSZachary Turner         : m_os_major(0)
29673b6e4fSZachary Turner         , m_os_minor(0)
30673b6e4fSZachary Turner         , m_os_update(0)
31673b6e4fSZachary Turner     {
32673b6e4fSZachary Turner     }
33673b6e4fSZachary Turner 
34673b6e4fSZachary Turner     std::string m_distribution_id;
35673b6e4fSZachary Turner     uint32_t m_os_major;
36673b6e4fSZachary Turner     uint32_t m_os_minor;
37673b6e4fSZachary Turner     uint32_t m_os_update;
38673b6e4fSZachary Turner };
39673b6e4fSZachary Turner 
40673b6e4fSZachary Turner HostInfoLinuxFields *g_fields = nullptr;
41673b6e4fSZachary Turner }
42673b6e4fSZachary Turner 
43673b6e4fSZachary Turner void
44673b6e4fSZachary Turner HostInfoLinux::Initialize()
45673b6e4fSZachary Turner {
46673b6e4fSZachary Turner     HostInfoPosix::Initialize();
47673b6e4fSZachary Turner 
48673b6e4fSZachary Turner     g_fields = new HostInfoLinuxFields();
49673b6e4fSZachary Turner }
5097a14e60SZachary Turner 
5139de3110SZachary Turner uint32_t
5239de3110SZachary Turner HostInfoLinux::GetMaxThreadNameLength()
5339de3110SZachary Turner {
5439de3110SZachary Turner     return 16;
5539de3110SZachary Turner }
5639de3110SZachary Turner 
5797a14e60SZachary Turner bool
5897a14e60SZachary Turner HostInfoLinux::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update)
5997a14e60SZachary Turner {
6097a14e60SZachary Turner     static bool success = false;
61*ff48e4beSGreg Clayton     static std::once_flag g_once_flag;
62*ff48e4beSGreg Clayton     std::call_once(g_once_flag,  []() {
6397a14e60SZachary Turner 
6497a14e60SZachary Turner         struct utsname un;
65*ff48e4beSGreg Clayton         if (uname(&un) == 0)
66*ff48e4beSGreg Clayton         {
67673b6e4fSZachary Turner             int status = sscanf(un.release, "%u.%u.%u", &g_fields->m_os_major, &g_fields->m_os_minor, &g_fields->m_os_update);
6897a14e60SZachary Turner             if (status == 3)
6997a14e60SZachary Turner                 success = true;
70*ff48e4beSGreg Clayton             else
71*ff48e4beSGreg Clayton             {
7297a14e60SZachary Turner                 // Some kernels omit the update version, so try looking for just "X.Y" and
7397a14e60SZachary Turner                 // set update to 0.
74673b6e4fSZachary Turner                 g_fields->m_os_update = 0;
75673b6e4fSZachary Turner                 status = sscanf(un.release, "%u.%u", &g_fields->m_os_major, &g_fields->m_os_minor);
76*ff48e4beSGreg Clayton                 if (status == 2)
77*ff48e4beSGreg Clayton                     success = true;
7897a14e60SZachary Turner             }
79*ff48e4beSGreg Clayton         }
80*ff48e4beSGreg Clayton     });
8197a14e60SZachary Turner 
82*ff48e4beSGreg Clayton 
83673b6e4fSZachary Turner     major = g_fields->m_os_major;
84673b6e4fSZachary Turner     minor = g_fields->m_os_minor;
85673b6e4fSZachary Turner     update = g_fields->m_os_update;
8697a14e60SZachary Turner     return success;
8797a14e60SZachary Turner }
8897a14e60SZachary Turner 
8953c038a5SOleksiy Vyalov bool
9053c038a5SOleksiy Vyalov HostInfoLinux::GetOSBuildString(std::string &s)
9153c038a5SOleksiy Vyalov {
9253c038a5SOleksiy Vyalov     struct utsname un;
9353c038a5SOleksiy Vyalov     ::memset(&un, 0, sizeof(utsname));
9453c038a5SOleksiy Vyalov     s.clear();
9553c038a5SOleksiy Vyalov 
9653c038a5SOleksiy Vyalov     if (uname(&un) < 0)
9753c038a5SOleksiy Vyalov         return false;
9853c038a5SOleksiy Vyalov 
9953c038a5SOleksiy Vyalov     s.assign(un.release);
10053c038a5SOleksiy Vyalov     return true;
10153c038a5SOleksiy Vyalov }
10253c038a5SOleksiy Vyalov 
10353c038a5SOleksiy Vyalov bool
10453c038a5SOleksiy Vyalov HostInfoLinux::GetOSKernelDescription(std::string &s)
10553c038a5SOleksiy Vyalov {
10653c038a5SOleksiy Vyalov     struct utsname un;
10753c038a5SOleksiy Vyalov 
10853c038a5SOleksiy Vyalov     ::memset(&un, 0, sizeof(utsname));
10953c038a5SOleksiy Vyalov     s.clear();
11053c038a5SOleksiy Vyalov 
11153c038a5SOleksiy Vyalov     if (uname(&un) < 0)
11253c038a5SOleksiy Vyalov         return false;
11353c038a5SOleksiy Vyalov 
11453c038a5SOleksiy Vyalov     s.assign(un.version);
11553c038a5SOleksiy Vyalov     return true;
11653c038a5SOleksiy Vyalov }
11753c038a5SOleksiy Vyalov 
11897a14e60SZachary Turner llvm::StringRef
11997a14e60SZachary Turner HostInfoLinux::GetDistributionId()
12097a14e60SZachary Turner {
12197a14e60SZachary Turner     // Try to run 'lbs_release -i', and use that response
12297a14e60SZachary Turner     // for the distribution id.
123*ff48e4beSGreg Clayton     static bool success = false;
124*ff48e4beSGreg Clayton     static std::once_flag g_once_flag;
125*ff48e4beSGreg Clayton     std::call_once(g_once_flag,  []() {
12697a14e60SZachary Turner 
12797a14e60SZachary Turner         Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST));
12897a14e60SZachary Turner         if (log)
12997a14e60SZachary Turner             log->Printf("attempting to determine Linux distribution...");
13097a14e60SZachary Turner 
13197a14e60SZachary Turner         // check if the lsb_release command exists at one of the
13297a14e60SZachary Turner         // following paths
13397a14e60SZachary Turner         const char *const exe_paths[] = {"/bin/lsb_release", "/usr/bin/lsb_release"};
13497a14e60SZachary Turner 
13597a14e60SZachary Turner         for (size_t exe_index = 0; exe_index < sizeof(exe_paths) / sizeof(exe_paths[0]); ++exe_index)
13697a14e60SZachary Turner         {
13797a14e60SZachary Turner             const char *const get_distribution_info_exe = exe_paths[exe_index];
13897a14e60SZachary Turner             if (access(get_distribution_info_exe, F_OK))
13997a14e60SZachary Turner             {
14097a14e60SZachary Turner                 // this exe doesn't exist, move on to next exe
14197a14e60SZachary Turner                 if (log)
14297a14e60SZachary Turner                     log->Printf("executable doesn't exist: %s", get_distribution_info_exe);
14397a14e60SZachary Turner                 continue;
14497a14e60SZachary Turner             }
14597a14e60SZachary Turner 
14697a14e60SZachary Turner             // execute the distribution-retrieval command, read output
14797a14e60SZachary Turner             std::string get_distribution_id_command(get_distribution_info_exe);
14897a14e60SZachary Turner             get_distribution_id_command += " -i";
14997a14e60SZachary Turner 
15097a14e60SZachary Turner             FILE *file = popen(get_distribution_id_command.c_str(), "r");
15197a14e60SZachary Turner             if (!file)
15297a14e60SZachary Turner             {
15397a14e60SZachary Turner                 if (log)
15497a14e60SZachary Turner                     log->Printf("failed to run command: \"%s\", cannot retrieve "
15597a14e60SZachary Turner                                 "platform information",
15697a14e60SZachary Turner                                 get_distribution_id_command.c_str());
15797a14e60SZachary Turner                 break;
15897a14e60SZachary Turner             }
15997a14e60SZachary Turner 
16097a14e60SZachary Turner             // retrieve the distribution id string.
16197a14e60SZachary Turner             char distribution_id[256] = {'\0'};
16297a14e60SZachary Turner             if (fgets(distribution_id, sizeof(distribution_id) - 1, file) != NULL)
16397a14e60SZachary Turner             {
16497a14e60SZachary Turner                 if (log)
16597a14e60SZachary Turner                     log->Printf("distribution id command returned \"%s\"", distribution_id);
16697a14e60SZachary Turner 
16797a14e60SZachary Turner                 const char *const distributor_id_key = "Distributor ID:\t";
16897a14e60SZachary Turner                 if (strstr(distribution_id, distributor_id_key))
16997a14e60SZachary Turner                 {
17097a14e60SZachary Turner                     // strip newlines
17197a14e60SZachary Turner                     std::string id_string(distribution_id + strlen(distributor_id_key));
17297a14e60SZachary Turner                     id_string.erase(std::remove(id_string.begin(), id_string.end(), '\n'), id_string.end());
17397a14e60SZachary Turner 
17497a14e60SZachary Turner                     // lower case it and convert whitespace to underscores
17597a14e60SZachary Turner                     std::transform(id_string.begin(), id_string.end(), id_string.begin(), [](char ch)
17697a14e60SZachary Turner                                    {
17797a14e60SZachary Turner                         return tolower(isspace(ch) ? '_' : ch);
17897a14e60SZachary Turner                     });
17997a14e60SZachary Turner 
180673b6e4fSZachary Turner                     g_fields->m_distribution_id = id_string;
18197a14e60SZachary Turner                     if (log)
182673b6e4fSZachary Turner                         log->Printf("distribution id set to \"%s\"", g_fields->m_distribution_id.c_str());
18397a14e60SZachary Turner                 }
18497a14e60SZachary Turner                 else
18597a14e60SZachary Turner                 {
18697a14e60SZachary Turner                     if (log)
18797a14e60SZachary Turner                         log->Printf("failed to find \"%s\" field in \"%s\"", distributor_id_key, distribution_id);
18897a14e60SZachary Turner                 }
18997a14e60SZachary Turner             }
19097a14e60SZachary Turner             else
19197a14e60SZachary Turner             {
19297a14e60SZachary Turner                 if (log)
19397a14e60SZachary Turner                     log->Printf("failed to retrieve distribution id, \"%s\" returned no"
19497a14e60SZachary Turner                                 " lines",
19597a14e60SZachary Turner                                 get_distribution_id_command.c_str());
19697a14e60SZachary Turner             }
19797a14e60SZachary Turner 
19897a14e60SZachary Turner             // clean up the file
19997a14e60SZachary Turner             pclose(file);
20097a14e60SZachary Turner         }
201*ff48e4beSGreg Clayton     });
20297a14e60SZachary Turner 
203673b6e4fSZachary Turner     return g_fields->m_distribution_id.c_str();
20497a14e60SZachary Turner }
20513b18261SZachary Turner 
206a21fee0eSZachary Turner FileSpec
207a21fee0eSZachary Turner HostInfoLinux::GetProgramFileSpec()
208a21fee0eSZachary Turner {
209a21fee0eSZachary Turner     static FileSpec g_program_filespec;
210a21fee0eSZachary Turner 
211a21fee0eSZachary Turner     if (!g_program_filespec)
212a21fee0eSZachary Turner     {
213a21fee0eSZachary Turner         char exe_path[PATH_MAX];
214a21fee0eSZachary Turner         ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1);
215a21fee0eSZachary Turner         if (len > 0)
216a21fee0eSZachary Turner         {
217a21fee0eSZachary Turner             exe_path[len] = 0;
218a21fee0eSZachary Turner             g_program_filespec.SetFile(exe_path, false);
219a21fee0eSZachary Turner         }
220a21fee0eSZachary Turner     }
221a21fee0eSZachary Turner 
222a21fee0eSZachary Turner     return g_program_filespec;
223a21fee0eSZachary Turner }
224a21fee0eSZachary Turner 
22542ff0ad8SZachary Turner bool
22642ff0ad8SZachary Turner HostInfoLinux::ComputeSystemPluginsDirectory(FileSpec &file_spec)
22742ff0ad8SZachary Turner {
2280d8400c8SGreg Clayton     FileSpec temp_file("/usr/lib/lldb", true);
2290d8400c8SGreg Clayton     file_spec.GetDirectory().SetCString(temp_file.GetPath().c_str());
23042ff0ad8SZachary Turner     return true;
23142ff0ad8SZachary Turner }
23242ff0ad8SZachary Turner 
23342ff0ad8SZachary Turner bool
23442ff0ad8SZachary Turner HostInfoLinux::ComputeUserPluginsDirectory(FileSpec &file_spec)
23542ff0ad8SZachary Turner {
23642ff0ad8SZachary Turner     // XDG Base Directory Specification
23742ff0ad8SZachary Turner     // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
23842ff0ad8SZachary Turner     // If XDG_DATA_HOME exists, use that, otherwise use ~/.local/share/lldb.
23942ff0ad8SZachary Turner     const char *xdg_data_home = getenv("XDG_DATA_HOME");
24042ff0ad8SZachary Turner     if (xdg_data_home && xdg_data_home[0])
24142ff0ad8SZachary Turner     {
24242ff0ad8SZachary Turner         std::string user_plugin_dir(xdg_data_home);
24342ff0ad8SZachary Turner         user_plugin_dir += "/lldb";
2440d8400c8SGreg Clayton         file_spec.GetDirectory().SetCString(user_plugin_dir.c_str());
24542ff0ad8SZachary Turner     }
24642ff0ad8SZachary Turner     else
2470d8400c8SGreg Clayton         file_spec.GetDirectory().SetCString("~/.local/share/lldb");
24842ff0ad8SZachary Turner     return true;
24942ff0ad8SZachary Turner }
25042ff0ad8SZachary Turner 
25113b18261SZachary Turner void
25213b18261SZachary Turner HostInfoLinux::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64)
25313b18261SZachary Turner {
25413b18261SZachary Turner     HostInfoPosix::ComputeHostArchitectureSupport(arch_32, arch_64);
25513b18261SZachary Turner 
25613b18261SZachary Turner     const char *distribution_id = GetDistributionId().data();
25713b18261SZachary Turner 
25813b18261SZachary Turner     // On Linux, "unknown" in the vendor slot isn't what we want for the default
25913b18261SZachary Turner     // triple.  It's probably an artifact of config.guess.
26013b18261SZachary Turner     if (arch_32.IsValid())
26113b18261SZachary Turner     {
26213b18261SZachary Turner         arch_32.SetDistributionId(distribution_id);
26313b18261SZachary Turner         if (arch_32.GetTriple().getVendor() == llvm::Triple::UnknownVendor)
26413b18261SZachary Turner             arch_32.GetTriple().setVendorName("");
26513b18261SZachary Turner     }
26613b18261SZachary Turner     if (arch_64.IsValid())
26713b18261SZachary Turner     {
26813b18261SZachary Turner         arch_64.SetDistributionId(distribution_id);
26913b18261SZachary Turner         if (arch_64.GetTriple().getVendor() == llvm::Triple::UnknownVendor)
27013b18261SZachary Turner             arch_64.GetTriple().setVendorName("");
27113b18261SZachary Turner     }
27213b18261SZachary Turner }
273