Home | History | Annotate | Line # | Download | only in Driver
      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