1d874057dim//===--- Distro.cpp - Linux distribution detection support ------*- C++ -*-===//
2d874057dim//
3d874057dim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4d874057dim// See https://llvm.org/LICENSE.txt for license information.
5d874057dim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6d874057dim//
7d874057dim//===----------------------------------------------------------------------===//
8d874057dim
9d874057dim#include "clang/Driver/Distro.h"
10d874057dim#include "clang/Basic/LLVM.h"
11d874057dim#include "llvm/ADT/SmallVector.h"
12d874057dim#include "llvm/ADT/StringRef.h"
13d874057dim#include "llvm/ADT/StringSwitch.h"
14d874057dim#include "llvm/Support/ErrorOr.h"
15d874057dim#include "llvm/Support/MemoryBuffer.h"
16332da66dim#include "llvm/ADT/Triple.h"
17d874057dim
18d874057dimusing namespace clang::driver;
19d874057dimusing namespace clang;
20d874057dim
21332da66dimstatic Distro::DistroType DetectDistro(llvm::vfs::FileSystem &VFS,
22332da66dim                                       const llvm::Triple &TargetOrHost) {
23332da66dim  // If we don't target Linux, no need to check the distro. This saves a few
24332da66dim  // OS calls.
25332da66dim  if (!TargetOrHost.isOSLinux())
26332da66dim    return Distro::UnknownDistro;
27332da66dim
28332da66dim  // If the host is not running Linux, and we're backed by a real file system,
29332da66dim  // no need to check the distro. This is the case where someone is
30332da66dim  // cross-compiling from BSD or Windows to Linux, and it would be meaningless
31332da66dim  // to try to figure out the "distro" of the non-Linux host.
32332da66dim  IntrusiveRefCntPtr<llvm::vfs::FileSystem> RealFS =
33332da66dim      llvm::vfs::getRealFileSystem();
34332da66dim  llvm::Triple HostTriple(llvm::sys::getProcessTriple());
35332da66dim  if (!HostTriple.isOSLinux() && &VFS == RealFS.get())
36332da66dim    return Distro::UnknownDistro;
37332da66dim
38d874057dim  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
39d874057dim      VFS.getBufferForFile("/etc/lsb-release");
40d874057dim  if (File) {
41d874057dim    StringRef Data = File.get()->getBuffer();
42d874057dim    SmallVector<StringRef, 16> Lines;
43d874057dim    Data.split(Lines, "\n");
44d874057dim    Distro::DistroType Version = Distro::UnknownDistro;
45d874057dim    for (StringRef Line : Lines)
46d874057dim      if (Version == Distro::UnknownDistro && Line.startswith("DISTRIB_CODENAME="))
47d874057dim        Version = llvm::StringSwitch<Distro::DistroType>(Line.substr(17))
48d874057dim                      .Case("hardy", Distro::UbuntuHardy)
49d874057dim                      .Case("intrepid", Distro::UbuntuIntrepid)
50d874057dim                      .Case("jaunty", Distro::UbuntuJaunty)
51d874057dim                      .Case("karmic", Distro::UbuntuKarmic)
52d874057dim                      .Case("lucid", Distro::UbuntuLucid)
53d874057dim                      .Case("maverick", Distro::UbuntuMaverick)
54d874057dim                      .Case("natty", Distro::UbuntuNatty)
55d874057dim                      .Case("oneiric", Distro::UbuntuOneiric)
56d874057dim                      .Case("precise", Distro::UbuntuPrecise)
57d874057dim                      .Case("quantal", Distro::UbuntuQuantal)
58d874057dim                      .Case("raring", Distro::UbuntuRaring)
59d874057dim                      .Case("saucy", Distro::UbuntuSaucy)
60d874057dim                      .Case("trusty", Distro::UbuntuTrusty)
61d874057dim                      .Case("utopic", Distro::UbuntuUtopic)
62d874057dim                      .Case("vivid", Distro::UbuntuVivid)
63d874057dim                      .Case("wily", Distro::UbuntuWily)
64d874057dim                      .Case("xenial", Distro::UbuntuXenial)
65d874057dim                      .Case("yakkety", Distro::UbuntuYakkety)
66d874057dim                      .Case("zesty", Distro::UbuntuZesty)
67d874057dim                      .Case("artful", Distro::UbuntuArtful)
68d874057dim                      .Case("bionic", Distro::UbuntuBionic)
69d874057dim                      .Case("cosmic", Distro::UbuntuCosmic)
70d874057dim                      .Case("disco", Distro::UbuntuDisco)
71d874057dim                      .Case("eoan", Distro::UbuntuEoan)
72332da66dim                      .Case("focal", Distro::UbuntuFocal)
73d874057dim                      .Default(Distro::UnknownDistro);
74d874057dim    if (Version != Distro::UnknownDistro)
75d874057dim      return Version;
76d874057dim  }
77d874057dim
78d874057dim  File = VFS.getBufferForFile("/etc/redhat-release");
79d874057dim  if (File) {
80d874057dim    StringRef Data = File.get()->getBuffer();
81d874057dim    if (Data.startswith("Fedora release"))
82d874057dim      return Distro::Fedora;
83d874057dim    if (Data.startswith("Red Hat Enterprise Linux") ||
84d874057dim        Data.startswith("CentOS") ||
85d874057dim        Data.startswith("Scientific Linux")) {
86d874057dim      if (Data.find("release 7") != StringRef::npos)
87d874057dim        return Distro::RHEL7;
88d874057dim      else if (Data.find("release 6") != StringRef::npos)
89d874057dim        return Distro::RHEL6;
90d874057dim      else if (Data.find("release 5") != StringRef::npos)
91d874057dim        return Distro::RHEL5;
92d874057dim    }
93d874057dim    return Distro::UnknownDistro;
94d874057dim  }
95d874057dim
96d874057dim  File = VFS.getBufferForFile("/etc/debian_version");
97d874057dim  if (File) {
98d874057dim    StringRef Data = File.get()->getBuffer();
99d874057dim    // Contents: < major.minor > or < codename/sid >
100d874057dim    int MajorVersion;
101d874057dim    if (!Data.split('.').first.getAsInteger(10, MajorVersion)) {
102d874057dim      switch (MajorVersion) {
103d874057dim      case 5:
104d874057dim        return Distro::DebianLenny;
105d874057dim      case 6:
106d874057dim        return Distro::DebianSqueeze;
107d874057dim      case 7:
108d874057dim        return Distro::DebianWheezy;
109d874057dim      case 8:
110d874057dim        return Distro::DebianJessie;
111d874057dim      case 9:
112d874057dim        return Distro::DebianStretch;
113d874057dim      case 10:
114d874057dim        return Distro::DebianBuster;
115d874057dim      case 11:
116d874057dim        return Distro::DebianBullseye;
117d874057dim      default:
118d874057dim        return Distro::UnknownDistro;
119d874057dim      }
120d874057dim    }
121d874057dim    return llvm::StringSwitch<Distro::DistroType>(Data.split("\n").first)
122d874057dim        .Case("squeeze/sid", Distro::DebianSqueeze)
123d874057dim        .Case("wheezy/sid", Distro::DebianWheezy)
124d874057dim        .Case("jessie/sid", Distro::DebianJessie)
125d874057dim        .Case("stretch/sid", Distro::DebianStretch)
126d874057dim        .Case("buster/sid", Distro::DebianBuster)
127d874057dim        .Case("bullseye/sid", Distro::DebianBullseye)
128d874057dim        .Default(Distro::UnknownDistro);
129d874057dim  }
130d874057dim
131d874057dim  File = VFS.getBufferForFile("/etc/SuSE-release");
132d874057dim  if (File) {
133d874057dim    StringRef Data = File.get()->getBuffer();
134d874057dim    SmallVector<StringRef, 8> Lines;
135d874057dim    Data.split(Lines, "\n");
136d874057dim    for (const StringRef& Line : Lines) {
137d874057dim      if (!Line.trim().startswith("VERSION"))
138d874057dim        continue;
139d874057dim      std::pair<StringRef, StringRef> SplitLine = Line.split('=');
140d874057dim      // Old versions have split VERSION and PATCHLEVEL
141d874057dim      // Newer versions use VERSION = x.y
142d874057dim      std::pair<StringRef, StringRef> SplitVer = SplitLine.second.trim().split('.');
143d874057dim      int Version;
144d874057dim
145d874057dim      // OpenSUSE/SLES 10 and older are not supported and not compatible
146d874057dim      // with our rules, so just treat them as Distro::UnknownDistro.
147d874057dim      if (!SplitVer.first.getAsInteger(10, Version) && Version > 10)
148d874057dim        return Distro::OpenSUSE;
149d874057dim      return Distro::UnknownDistro;
150d874057dim    }
151d874057dim    return Distro::UnknownDistro;
152d874057dim  }
153d874057dim
154d874057dim  if (VFS.exists("/etc/exherbo-release"))
155d874057dim    return Distro::Exherbo;
156d874057dim
157d874057dim  if (VFS.exists("/etc/alpine-release"))
158d874057dim    return Distro::AlpineLinux;
159d874057dim
160d874057dim  if (VFS.exists("/etc/arch-release"))
161d874057dim    return Distro::ArchLinux;
162d874057dim
163d874057dim  if (VFS.exists("/etc/gentoo-release"))
164d874057dim    return Distro::Gentoo;
165d874057dim
166d874057dim  return Distro::UnknownDistro;
167d874057dim}
168d874057dim
169332da66dimDistro::Distro(llvm::vfs::FileSystem &VFS, const llvm::Triple &TargetOrHost)
170332da66dim    : DistroVal(DetectDistro(VFS, TargetOrHost)) {}
171