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>
1997a14e60SZachary Turner 
2097a14e60SZachary Turner using namespace lldb_private;
2197a14e60SZachary Turner 
22673b6e4fSZachary Turner namespace
23673b6e4fSZachary Turner {
24673b6e4fSZachary Turner struct HostInfoLinuxFields
25673b6e4fSZachary Turner {
26673b6e4fSZachary Turner     HostInfoLinuxFields()
27673b6e4fSZachary Turner         : m_os_major(0)
28673b6e4fSZachary Turner         , m_os_minor(0)
29673b6e4fSZachary Turner         , m_os_update(0)
30673b6e4fSZachary Turner     {
31673b6e4fSZachary Turner     }
32673b6e4fSZachary Turner 
33673b6e4fSZachary Turner     std::string m_distribution_id;
34673b6e4fSZachary Turner     uint32_t m_os_major;
35673b6e4fSZachary Turner     uint32_t m_os_minor;
36673b6e4fSZachary Turner     uint32_t m_os_update;
37673b6e4fSZachary Turner };
38673b6e4fSZachary Turner 
39673b6e4fSZachary Turner HostInfoLinuxFields *g_fields = nullptr;
40673b6e4fSZachary Turner }
41673b6e4fSZachary Turner 
42673b6e4fSZachary Turner void
43673b6e4fSZachary Turner HostInfoLinux::Initialize()
44673b6e4fSZachary Turner {
45673b6e4fSZachary Turner     HostInfoPosix::Initialize();
46673b6e4fSZachary Turner 
47673b6e4fSZachary Turner     g_fields = new HostInfoLinuxFields();
48673b6e4fSZachary Turner }
4997a14e60SZachary Turner 
5097a14e60SZachary Turner bool
5197a14e60SZachary Turner HostInfoLinux::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update)
5297a14e60SZachary Turner {
5397a14e60SZachary Turner     static bool is_initialized = false;
5497a14e60SZachary Turner     static bool success = false;
5597a14e60SZachary Turner 
5697a14e60SZachary Turner     if (!is_initialized)
5797a14e60SZachary Turner     {
5897a14e60SZachary Turner         is_initialized = true;
5997a14e60SZachary Turner         struct utsname un;
6097a14e60SZachary Turner 
6197a14e60SZachary Turner         if (uname(&un))
6297a14e60SZachary Turner             goto finished;
6397a14e60SZachary Turner 
64673b6e4fSZachary Turner         int status = sscanf(un.release, "%u.%u.%u", &g_fields->m_os_major, &g_fields->m_os_minor, &g_fields->m_os_update);
6597a14e60SZachary Turner         if (status == 3)
6697a14e60SZachary Turner         {
6797a14e60SZachary Turner             success = true;
6897a14e60SZachary Turner             goto finished;
6997a14e60SZachary Turner         }
7097a14e60SZachary Turner 
7197a14e60SZachary Turner         // Some kernels omit the update version, so try looking for just "X.Y" and
7297a14e60SZachary Turner         // set update to 0.
73673b6e4fSZachary Turner         g_fields->m_os_update = 0;
74673b6e4fSZachary Turner         status = sscanf(un.release, "%u.%u", &g_fields->m_os_major, &g_fields->m_os_minor);
7597a14e60SZachary Turner         success = !!(status == 2);
7697a14e60SZachary Turner     }
7797a14e60SZachary Turner 
7897a14e60SZachary Turner finished:
79673b6e4fSZachary Turner     major = g_fields->m_os_major;
80673b6e4fSZachary Turner     minor = g_fields->m_os_minor;
81673b6e4fSZachary Turner     update = g_fields->m_os_update;
8297a14e60SZachary Turner     return success;
8397a14e60SZachary Turner }
8497a14e60SZachary Turner 
8597a14e60SZachary Turner llvm::StringRef
8697a14e60SZachary Turner HostInfoLinux::GetDistributionId()
8797a14e60SZachary Turner {
8897a14e60SZachary Turner     static bool is_initialized = false;
8997a14e60SZachary Turner     // Try to run 'lbs_release -i', and use that response
9097a14e60SZachary Turner     // for the distribution id.
9197a14e60SZachary Turner 
9297a14e60SZachary Turner     if (!is_initialized)
9397a14e60SZachary Turner     {
9497a14e60SZachary Turner         is_initialized = true;
9597a14e60SZachary Turner 
9697a14e60SZachary Turner         Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST));
9797a14e60SZachary Turner         if (log)
9897a14e60SZachary Turner             log->Printf("attempting to determine Linux distribution...");
9997a14e60SZachary Turner 
10097a14e60SZachary Turner         // check if the lsb_release command exists at one of the
10197a14e60SZachary Turner         // following paths
10297a14e60SZachary Turner         const char *const exe_paths[] = {"/bin/lsb_release", "/usr/bin/lsb_release"};
10397a14e60SZachary Turner 
10497a14e60SZachary Turner         for (size_t exe_index = 0; exe_index < sizeof(exe_paths) / sizeof(exe_paths[0]); ++exe_index)
10597a14e60SZachary Turner         {
10697a14e60SZachary Turner             const char *const get_distribution_info_exe = exe_paths[exe_index];
10797a14e60SZachary Turner             if (access(get_distribution_info_exe, F_OK))
10897a14e60SZachary Turner             {
10997a14e60SZachary Turner                 // this exe doesn't exist, move on to next exe
11097a14e60SZachary Turner                 if (log)
11197a14e60SZachary Turner                     log->Printf("executable doesn't exist: %s", get_distribution_info_exe);
11297a14e60SZachary Turner                 continue;
11397a14e60SZachary Turner             }
11497a14e60SZachary Turner 
11597a14e60SZachary Turner             // execute the distribution-retrieval command, read output
11697a14e60SZachary Turner             std::string get_distribution_id_command(get_distribution_info_exe);
11797a14e60SZachary Turner             get_distribution_id_command += " -i";
11897a14e60SZachary Turner 
11997a14e60SZachary Turner             FILE *file = popen(get_distribution_id_command.c_str(), "r");
12097a14e60SZachary Turner             if (!file)
12197a14e60SZachary Turner             {
12297a14e60SZachary Turner                 if (log)
12397a14e60SZachary Turner                     log->Printf("failed to run command: \"%s\", cannot retrieve "
12497a14e60SZachary Turner                                 "platform information",
12597a14e60SZachary Turner                                 get_distribution_id_command.c_str());
12697a14e60SZachary Turner                 break;
12797a14e60SZachary Turner             }
12897a14e60SZachary Turner 
12997a14e60SZachary Turner             // retrieve the distribution id string.
13097a14e60SZachary Turner             char distribution_id[256] = {'\0'};
13197a14e60SZachary Turner             if (fgets(distribution_id, sizeof(distribution_id) - 1, file) != NULL)
13297a14e60SZachary Turner             {
13397a14e60SZachary Turner                 if (log)
13497a14e60SZachary Turner                     log->Printf("distribution id command returned \"%s\"", distribution_id);
13597a14e60SZachary Turner 
13697a14e60SZachary Turner                 const char *const distributor_id_key = "Distributor ID:\t";
13797a14e60SZachary Turner                 if (strstr(distribution_id, distributor_id_key))
13897a14e60SZachary Turner                 {
13997a14e60SZachary Turner                     // strip newlines
14097a14e60SZachary Turner                     std::string id_string(distribution_id + strlen(distributor_id_key));
14197a14e60SZachary Turner                     id_string.erase(std::remove(id_string.begin(), id_string.end(), '\n'), id_string.end());
14297a14e60SZachary Turner 
14397a14e60SZachary Turner                     // lower case it and convert whitespace to underscores
14497a14e60SZachary Turner                     std::transform(id_string.begin(), id_string.end(), id_string.begin(), [](char ch)
14597a14e60SZachary Turner                                    {
14697a14e60SZachary Turner                         return tolower(isspace(ch) ? '_' : ch);
14797a14e60SZachary Turner                     });
14897a14e60SZachary Turner 
149673b6e4fSZachary Turner                     g_fields->m_distribution_id = id_string;
15097a14e60SZachary Turner                     if (log)
151673b6e4fSZachary Turner                         log->Printf("distribution id set to \"%s\"", g_fields->m_distribution_id.c_str());
15297a14e60SZachary Turner                 }
15397a14e60SZachary Turner                 else
15497a14e60SZachary Turner                 {
15597a14e60SZachary Turner                     if (log)
15697a14e60SZachary Turner                         log->Printf("failed to find \"%s\" field in \"%s\"", distributor_id_key, distribution_id);
15797a14e60SZachary Turner                 }
15897a14e60SZachary Turner             }
15997a14e60SZachary Turner             else
16097a14e60SZachary Turner             {
16197a14e60SZachary Turner                 if (log)
16297a14e60SZachary Turner                     log->Printf("failed to retrieve distribution id, \"%s\" returned no"
16397a14e60SZachary Turner                                 " lines",
16497a14e60SZachary Turner                                 get_distribution_id_command.c_str());
16597a14e60SZachary Turner             }
16697a14e60SZachary Turner 
16797a14e60SZachary Turner             // clean up the file
16897a14e60SZachary Turner             pclose(file);
16997a14e60SZachary Turner         }
17097a14e60SZachary Turner     }
17197a14e60SZachary Turner 
172673b6e4fSZachary Turner     return g_fields->m_distribution_id.c_str();
17397a14e60SZachary Turner }
17413b18261SZachary Turner 
175a21fee0eSZachary Turner FileSpec
176a21fee0eSZachary Turner HostInfoLinux::GetProgramFileSpec()
177a21fee0eSZachary Turner {
178a21fee0eSZachary Turner     static FileSpec g_program_filespec;
179a21fee0eSZachary Turner 
180a21fee0eSZachary Turner     if (!g_program_filespec)
181a21fee0eSZachary Turner     {
182a21fee0eSZachary Turner         char exe_path[PATH_MAX];
183a21fee0eSZachary Turner         ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1);
184a21fee0eSZachary Turner         if (len > 0)
185a21fee0eSZachary Turner         {
186a21fee0eSZachary Turner             exe_path[len] = 0;
187a21fee0eSZachary Turner             g_program_filespec.SetFile(exe_path, false);
188a21fee0eSZachary Turner         }
189a21fee0eSZachary Turner     }
190a21fee0eSZachary Turner 
191a21fee0eSZachary Turner     return g_program_filespec;
192a21fee0eSZachary Turner }
193a21fee0eSZachary Turner 
19442ff0ad8SZachary Turner bool
19542ff0ad8SZachary Turner HostInfoLinux::ComputeSystemPluginsDirectory(FileSpec &file_spec)
19642ff0ad8SZachary Turner {
197*0d8400c8SGreg Clayton     FileSpec temp_file("/usr/lib/lldb", true);
198*0d8400c8SGreg Clayton     file_spec.GetDirectory().SetCString(temp_file.GetPath().c_str());
19942ff0ad8SZachary Turner     return true;
20042ff0ad8SZachary Turner }
20142ff0ad8SZachary Turner 
20242ff0ad8SZachary Turner bool
20342ff0ad8SZachary Turner HostInfoLinux::ComputeUserPluginsDirectory(FileSpec &file_spec)
20442ff0ad8SZachary Turner {
20542ff0ad8SZachary Turner     // XDG Base Directory Specification
20642ff0ad8SZachary Turner     // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
20742ff0ad8SZachary Turner     // If XDG_DATA_HOME exists, use that, otherwise use ~/.local/share/lldb.
20842ff0ad8SZachary Turner     const char *xdg_data_home = getenv("XDG_DATA_HOME");
20942ff0ad8SZachary Turner     if (xdg_data_home && xdg_data_home[0])
21042ff0ad8SZachary Turner     {
21142ff0ad8SZachary Turner         std::string user_plugin_dir(xdg_data_home);
21242ff0ad8SZachary Turner         user_plugin_dir += "/lldb";
213*0d8400c8SGreg Clayton         file_spec.GetDirectory().SetCString(user_plugin_dir.c_str());
21442ff0ad8SZachary Turner     }
21542ff0ad8SZachary Turner     else
216*0d8400c8SGreg Clayton         file_spec.GetDirectory().SetCString("~/.local/share/lldb");
21742ff0ad8SZachary Turner     return true;
21842ff0ad8SZachary Turner }
21942ff0ad8SZachary Turner 
22013b18261SZachary Turner void
22113b18261SZachary Turner HostInfoLinux::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64)
22213b18261SZachary Turner {
22313b18261SZachary Turner     HostInfoPosix::ComputeHostArchitectureSupport(arch_32, arch_64);
22413b18261SZachary Turner 
22513b18261SZachary Turner     const char *distribution_id = GetDistributionId().data();
22613b18261SZachary Turner 
22713b18261SZachary Turner     // On Linux, "unknown" in the vendor slot isn't what we want for the default
22813b18261SZachary Turner     // triple.  It's probably an artifact of config.guess.
22913b18261SZachary Turner     if (arch_32.IsValid())
23013b18261SZachary Turner     {
23113b18261SZachary Turner         arch_32.SetDistributionId(distribution_id);
23213b18261SZachary Turner         if (arch_32.GetTriple().getVendor() == llvm::Triple::UnknownVendor)
23313b18261SZachary Turner             arch_32.GetTriple().setVendorName("");
23413b18261SZachary Turner     }
23513b18261SZachary Turner     if (arch_64.IsValid())
23613b18261SZachary Turner     {
23713b18261SZachary Turner         arch_64.SetDistributionId(distribution_id);
23813b18261SZachary Turner         if (arch_64.GetTriple().getVendor() == llvm::Triple::UnknownVendor)
23913b18261SZachary Turner             arch_64.GetTriple().setVendorName("");
24013b18261SZachary Turner     }
24113b18261SZachary Turner }
242