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