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 <stdio.h>
14 #include <string.h>
15 #include <sys/utsname.h>
16 
17 #include <algorithm>
18 
19 using namespace lldb_private;
20 
21 std::string HostInfoLinux::m_distribution_id;
22 uint32_t HostInfoLinux::m_os_major = 0;
23 uint32_t HostInfoLinux::m_os_minor = 0;
24 uint32_t HostInfoLinux::m_os_update = 0;
25 
26 bool
27 HostInfoLinux::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update)
28 {
29     static bool is_initialized = false;
30     static bool success = false;
31 
32     if (!is_initialized)
33     {
34         is_initialized = true;
35         struct utsname un;
36 
37         if (uname(&un))
38             goto finished;
39 
40         int status = sscanf(un.release, "%u.%u.%u", &major, &minor, &update);
41         if (status == 3)
42         {
43             success = true;
44             goto finished;
45         }
46 
47         // Some kernels omit the update version, so try looking for just "X.Y" and
48         // set update to 0.
49         update = 0;
50         status = sscanf(un.release, "%u.%u", &major, &minor);
51         success = !!(status == 2);
52     }
53 
54 finished:
55     major = m_os_major;
56     minor = m_os_minor;
57     update = m_os_update;
58     return success;
59 }
60 
61 llvm::StringRef
62 HostInfoLinux::GetDistributionId()
63 {
64     static bool is_initialized = false;
65     // Try to run 'lbs_release -i', and use that response
66     // for the distribution id.
67 
68     if (!is_initialized)
69     {
70         is_initialized = true;
71 
72         Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST));
73         if (log)
74             log->Printf("attempting to determine Linux distribution...");
75 
76         // check if the lsb_release command exists at one of the
77         // following paths
78         const char *const exe_paths[] = {"/bin/lsb_release", "/usr/bin/lsb_release"};
79 
80         for (size_t exe_index = 0; exe_index < sizeof(exe_paths) / sizeof(exe_paths[0]); ++exe_index)
81         {
82             const char *const get_distribution_info_exe = exe_paths[exe_index];
83             if (access(get_distribution_info_exe, F_OK))
84             {
85                 // this exe doesn't exist, move on to next exe
86                 if (log)
87                     log->Printf("executable doesn't exist: %s", get_distribution_info_exe);
88                 continue;
89             }
90 
91             // execute the distribution-retrieval command, read output
92             std::string get_distribution_id_command(get_distribution_info_exe);
93             get_distribution_id_command += " -i";
94 
95             FILE *file = popen(get_distribution_id_command.c_str(), "r");
96             if (!file)
97             {
98                 if (log)
99                     log->Printf("failed to run command: \"%s\", cannot retrieve "
100                                 "platform information",
101                                 get_distribution_id_command.c_str());
102                 break;
103             }
104 
105             // retrieve the distribution id string.
106             char distribution_id[256] = {'\0'};
107             if (fgets(distribution_id, sizeof(distribution_id) - 1, file) != NULL)
108             {
109                 if (log)
110                     log->Printf("distribution id command returned \"%s\"", distribution_id);
111 
112                 const char *const distributor_id_key = "Distributor ID:\t";
113                 if (strstr(distribution_id, distributor_id_key))
114                 {
115                     // strip newlines
116                     std::string id_string(distribution_id + strlen(distributor_id_key));
117                     id_string.erase(std::remove(id_string.begin(), id_string.end(), '\n'), id_string.end());
118 
119                     // lower case it and convert whitespace to underscores
120                     std::transform(id_string.begin(), id_string.end(), id_string.begin(), [](char ch)
121                                    {
122                         return tolower(isspace(ch) ? '_' : ch);
123                     });
124 
125                     m_distribution_id = id_string;
126                     if (log)
127                         log->Printf("distribution id set to \"%s\"", m_distribution_id.c_str());
128                 }
129                 else
130                 {
131                     if (log)
132                         log->Printf("failed to find \"%s\" field in \"%s\"", distributor_id_key, distribution_id);
133                 }
134             }
135             else
136             {
137                 if (log)
138                     log->Printf("failed to retrieve distribution id, \"%s\" returned no"
139                                 " lines",
140                                 get_distribution_id_command.c_str());
141             }
142 
143             // clean up the file
144             pclose(file);
145         }
146     }
147 
148     return m_distribution_id.c_str();
149 }
150