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