Distro.cpp revision 1.1.1.2 1 1.1 joerg //===--- Distro.cpp - Linux distribution detection support ------*- C++ -*-===//
2 1.1 joerg //
3 1.1 joerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 1.1 joerg // See https://llvm.org/LICENSE.txt for license information.
5 1.1 joerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 1.1 joerg //
7 1.1 joerg //===----------------------------------------------------------------------===//
8 1.1 joerg
9 1.1 joerg #include "clang/Driver/Distro.h"
10 1.1 joerg #include "clang/Basic/LLVM.h"
11 1.1 joerg #include "llvm/ADT/SmallVector.h"
12 1.1 joerg #include "llvm/ADT/StringRef.h"
13 1.1 joerg #include "llvm/ADT/StringSwitch.h"
14 1.1.1.2 joerg #include "llvm/ADT/Triple.h"
15 1.1 joerg #include "llvm/Support/ErrorOr.h"
16 1.1.1.2 joerg #include "llvm/Support/Host.h"
17 1.1 joerg #include "llvm/Support/MemoryBuffer.h"
18 1.1.1.2 joerg #include "llvm/Support/Threading.h"
19 1.1 joerg
20 1.1 joerg using namespace clang::driver;
21 1.1 joerg using namespace clang;
22 1.1 joerg
23 1.1.1.2 joerg static Distro::DistroType DetectOsRelease(llvm::vfs::FileSystem &VFS) {
24 1.1.1.2 joerg llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
25 1.1.1.2 joerg VFS.getBufferForFile("/etc/os-release");
26 1.1.1.2 joerg if (!File)
27 1.1.1.2 joerg File = VFS.getBufferForFile("/usr/lib/os-release");
28 1.1.1.2 joerg if (!File)
29 1.1.1.2 joerg return Distro::UnknownDistro;
30 1.1.1.2 joerg
31 1.1.1.2 joerg SmallVector<StringRef, 16> Lines;
32 1.1.1.2 joerg File.get()->getBuffer().split(Lines, "\n");
33 1.1.1.2 joerg Distro::DistroType Version = Distro::UnknownDistro;
34 1.1.1.2 joerg
35 1.1.1.2 joerg // Obviously this can be improved a lot.
36 1.1.1.2 joerg for (StringRef Line : Lines)
37 1.1.1.2 joerg if (Version == Distro::UnknownDistro && Line.startswith("ID="))
38 1.1.1.2 joerg Version = llvm::StringSwitch<Distro::DistroType>(Line.substr(3))
39 1.1.1.2 joerg .Case("alpine", Distro::AlpineLinux)
40 1.1.1.2 joerg .Case("fedora", Distro::Fedora)
41 1.1.1.2 joerg .Case("gentoo", Distro::Gentoo)
42 1.1.1.2 joerg .Case("arch", Distro::ArchLinux)
43 1.1.1.2 joerg // On SLES, /etc/os-release was introduced in SLES 11.
44 1.1.1.2 joerg .Case("sles", Distro::OpenSUSE)
45 1.1.1.2 joerg .Case("opensuse", Distro::OpenSUSE)
46 1.1.1.2 joerg .Case("exherbo", Distro::Exherbo)
47 1.1.1.2 joerg .Default(Distro::UnknownDistro);
48 1.1.1.2 joerg return Version;
49 1.1.1.2 joerg }
50 1.1.1.2 joerg
51 1.1.1.2 joerg static Distro::DistroType DetectLsbRelease(llvm::vfs::FileSystem &VFS) {
52 1.1 joerg llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
53 1.1 joerg VFS.getBufferForFile("/etc/lsb-release");
54 1.1.1.2 joerg if (!File)
55 1.1.1.2 joerg return Distro::UnknownDistro;
56 1.1.1.2 joerg
57 1.1.1.2 joerg SmallVector<StringRef, 16> Lines;
58 1.1.1.2 joerg File.get()->getBuffer().split(Lines, "\n");
59 1.1.1.2 joerg Distro::DistroType Version = Distro::UnknownDistro;
60 1.1.1.2 joerg
61 1.1.1.2 joerg for (StringRef Line : Lines)
62 1.1.1.2 joerg if (Version == Distro::UnknownDistro &&
63 1.1.1.2 joerg Line.startswith("DISTRIB_CODENAME="))
64 1.1.1.2 joerg Version = llvm::StringSwitch<Distro::DistroType>(Line.substr(17))
65 1.1.1.2 joerg .Case("hardy", Distro::UbuntuHardy)
66 1.1.1.2 joerg .Case("intrepid", Distro::UbuntuIntrepid)
67 1.1.1.2 joerg .Case("jaunty", Distro::UbuntuJaunty)
68 1.1.1.2 joerg .Case("karmic", Distro::UbuntuKarmic)
69 1.1.1.2 joerg .Case("lucid", Distro::UbuntuLucid)
70 1.1.1.2 joerg .Case("maverick", Distro::UbuntuMaverick)
71 1.1.1.2 joerg .Case("natty", Distro::UbuntuNatty)
72 1.1.1.2 joerg .Case("oneiric", Distro::UbuntuOneiric)
73 1.1.1.2 joerg .Case("precise", Distro::UbuntuPrecise)
74 1.1.1.2 joerg .Case("quantal", Distro::UbuntuQuantal)
75 1.1.1.2 joerg .Case("raring", Distro::UbuntuRaring)
76 1.1.1.2 joerg .Case("saucy", Distro::UbuntuSaucy)
77 1.1.1.2 joerg .Case("trusty", Distro::UbuntuTrusty)
78 1.1.1.2 joerg .Case("utopic", Distro::UbuntuUtopic)
79 1.1.1.2 joerg .Case("vivid", Distro::UbuntuVivid)
80 1.1.1.2 joerg .Case("wily", Distro::UbuntuWily)
81 1.1.1.2 joerg .Case("xenial", Distro::UbuntuXenial)
82 1.1.1.2 joerg .Case("yakkety", Distro::UbuntuYakkety)
83 1.1.1.2 joerg .Case("zesty", Distro::UbuntuZesty)
84 1.1.1.2 joerg .Case("artful", Distro::UbuntuArtful)
85 1.1.1.2 joerg .Case("bionic", Distro::UbuntuBionic)
86 1.1.1.2 joerg .Case("cosmic", Distro::UbuntuCosmic)
87 1.1.1.2 joerg .Case("disco", Distro::UbuntuDisco)
88 1.1.1.2 joerg .Case("eoan", Distro::UbuntuEoan)
89 1.1.1.2 joerg .Case("focal", Distro::UbuntuFocal)
90 1.1.1.2 joerg .Case("groovy", Distro::UbuntuGroovy)
91 1.1.1.2 joerg .Case("hirsute", Distro::UbuntuHirsute)
92 1.1.1.2 joerg .Case("impish", Distro::UbuntuImpish)
93 1.1.1.2 joerg .Default(Distro::UnknownDistro);
94 1.1.1.2 joerg return Version;
95 1.1.1.2 joerg }
96 1.1.1.2 joerg
97 1.1.1.2 joerg static Distro::DistroType DetectDistro(llvm::vfs::FileSystem &VFS) {
98 1.1.1.2 joerg Distro::DistroType Version = Distro::UnknownDistro;
99 1.1.1.2 joerg
100 1.1.1.2 joerg // Newer freedesktop.org's compilant systemd-based systems
101 1.1.1.2 joerg // should provide /etc/os-release or /usr/lib/os-release.
102 1.1.1.2 joerg Version = DetectOsRelease(VFS);
103 1.1.1.2 joerg if (Version != Distro::UnknownDistro)
104 1.1.1.2 joerg return Version;
105 1.1.1.2 joerg
106 1.1.1.2 joerg // Older systems might provide /etc/lsb-release.
107 1.1.1.2 joerg Version = DetectLsbRelease(VFS);
108 1.1.1.2 joerg if (Version != Distro::UnknownDistro)
109 1.1.1.2 joerg return Version;
110 1.1.1.2 joerg
111 1.1.1.2 joerg // Otherwise try some distro-specific quirks for RedHat...
112 1.1.1.2 joerg llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
113 1.1.1.2 joerg VFS.getBufferForFile("/etc/redhat-release");
114 1.1 joerg
115 1.1 joerg if (File) {
116 1.1 joerg StringRef Data = File.get()->getBuffer();
117 1.1 joerg if (Data.startswith("Fedora release"))
118 1.1 joerg return Distro::Fedora;
119 1.1 joerg if (Data.startswith("Red Hat Enterprise Linux") ||
120 1.1.1.2 joerg Data.startswith("CentOS") || Data.startswith("Scientific Linux")) {
121 1.1 joerg if (Data.find("release 7") != StringRef::npos)
122 1.1 joerg return Distro::RHEL7;
123 1.1 joerg else if (Data.find("release 6") != StringRef::npos)
124 1.1 joerg return Distro::RHEL6;
125 1.1 joerg else if (Data.find("release 5") != StringRef::npos)
126 1.1 joerg return Distro::RHEL5;
127 1.1 joerg }
128 1.1 joerg return Distro::UnknownDistro;
129 1.1 joerg }
130 1.1 joerg
131 1.1.1.2 joerg // ...for Debian
132 1.1 joerg File = VFS.getBufferForFile("/etc/debian_version");
133 1.1 joerg if (File) {
134 1.1 joerg StringRef Data = File.get()->getBuffer();
135 1.1 joerg // Contents: < major.minor > or < codename/sid >
136 1.1 joerg int MajorVersion;
137 1.1 joerg if (!Data.split('.').first.getAsInteger(10, MajorVersion)) {
138 1.1 joerg switch (MajorVersion) {
139 1.1 joerg case 5:
140 1.1 joerg return Distro::DebianLenny;
141 1.1 joerg case 6:
142 1.1 joerg return Distro::DebianSqueeze;
143 1.1 joerg case 7:
144 1.1 joerg return Distro::DebianWheezy;
145 1.1 joerg case 8:
146 1.1 joerg return Distro::DebianJessie;
147 1.1 joerg case 9:
148 1.1 joerg return Distro::DebianStretch;
149 1.1 joerg case 10:
150 1.1 joerg return Distro::DebianBuster;
151 1.1 joerg case 11:
152 1.1 joerg return Distro::DebianBullseye;
153 1.1 joerg default:
154 1.1 joerg return Distro::UnknownDistro;
155 1.1 joerg }
156 1.1 joerg }
157 1.1 joerg return llvm::StringSwitch<Distro::DistroType>(Data.split("\n").first)
158 1.1 joerg .Case("squeeze/sid", Distro::DebianSqueeze)
159 1.1 joerg .Case("wheezy/sid", Distro::DebianWheezy)
160 1.1 joerg .Case("jessie/sid", Distro::DebianJessie)
161 1.1 joerg .Case("stretch/sid", Distro::DebianStretch)
162 1.1 joerg .Case("buster/sid", Distro::DebianBuster)
163 1.1 joerg .Case("bullseye/sid", Distro::DebianBullseye)
164 1.1 joerg .Default(Distro::UnknownDistro);
165 1.1 joerg }
166 1.1 joerg
167 1.1.1.2 joerg // ...for SUSE
168 1.1 joerg File = VFS.getBufferForFile("/etc/SuSE-release");
169 1.1 joerg if (File) {
170 1.1 joerg StringRef Data = File.get()->getBuffer();
171 1.1 joerg SmallVector<StringRef, 8> Lines;
172 1.1 joerg Data.split(Lines, "\n");
173 1.1.1.2 joerg for (const StringRef &Line : Lines) {
174 1.1 joerg if (!Line.trim().startswith("VERSION"))
175 1.1 joerg continue;
176 1.1 joerg std::pair<StringRef, StringRef> SplitLine = Line.split('=');
177 1.1 joerg // Old versions have split VERSION and PATCHLEVEL
178 1.1 joerg // Newer versions use VERSION = x.y
179 1.1.1.2 joerg std::pair<StringRef, StringRef> SplitVer =
180 1.1.1.2 joerg SplitLine.second.trim().split('.');
181 1.1 joerg int Version;
182 1.1 joerg
183 1.1 joerg // OpenSUSE/SLES 10 and older are not supported and not compatible
184 1.1 joerg // with our rules, so just treat them as Distro::UnknownDistro.
185 1.1 joerg if (!SplitVer.first.getAsInteger(10, Version) && Version > 10)
186 1.1 joerg return Distro::OpenSUSE;
187 1.1 joerg return Distro::UnknownDistro;
188 1.1 joerg }
189 1.1 joerg return Distro::UnknownDistro;
190 1.1 joerg }
191 1.1 joerg
192 1.1.1.2 joerg // ...and others.
193 1.1 joerg if (VFS.exists("/etc/gentoo-release"))
194 1.1 joerg return Distro::Gentoo;
195 1.1 joerg
196 1.1 joerg return Distro::UnknownDistro;
197 1.1 joerg }
198 1.1 joerg
199 1.1.1.2 joerg static Distro::DistroType GetDistro(llvm::vfs::FileSystem &VFS,
200 1.1.1.2 joerg const llvm::Triple &TargetOrHost) {
201 1.1.1.2 joerg // If we don't target Linux, no need to check the distro. This saves a few
202 1.1.1.2 joerg // OS calls.
203 1.1.1.2 joerg if (!TargetOrHost.isOSLinux())
204 1.1.1.2 joerg return Distro::UnknownDistro;
205 1.1.1.2 joerg
206 1.1.1.2 joerg // True if we're backed by a real file system.
207 1.1.1.2 joerg const bool onRealFS = (llvm::vfs::getRealFileSystem() == &VFS);
208 1.1.1.2 joerg
209 1.1.1.2 joerg // If the host is not running Linux, and we're backed by a real file
210 1.1.1.2 joerg // system, no need to check the distro. This is the case where someone
211 1.1.1.2 joerg // is cross-compiling from BSD or Windows to Linux, and it would be
212 1.1.1.2 joerg // meaningless to try to figure out the "distro" of the non-Linux host.
213 1.1.1.2 joerg llvm::Triple HostTriple(llvm::sys::getProcessTriple());
214 1.1.1.2 joerg if (!HostTriple.isOSLinux() && onRealFS)
215 1.1.1.2 joerg return Distro::UnknownDistro;
216 1.1.1.2 joerg
217 1.1.1.2 joerg if (onRealFS) {
218 1.1.1.2 joerg // If we're backed by a real file system, perform
219 1.1.1.2 joerg // the detection only once and save the result.
220 1.1.1.2 joerg static Distro::DistroType LinuxDistro = DetectDistro(VFS);
221 1.1.1.2 joerg return LinuxDistro;
222 1.1.1.2 joerg }
223 1.1.1.2 joerg // This is mostly for passing tests which uses llvm::vfs::InMemoryFileSystem,
224 1.1.1.2 joerg // which is not "real".
225 1.1.1.2 joerg return DetectDistro(VFS);
226 1.1.1.2 joerg }
227 1.1.1.2 joerg
228 1.1.1.2 joerg Distro::Distro(llvm::vfs::FileSystem &VFS, const llvm::Triple &TargetOrHost)
229 1.1.1.2 joerg : DistroVal(GetDistro(VFS, TargetOrHost)) {}
230