From 82208be49eecc07f9c6a38365876c28273529192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 11 Mar 2018 23:00:54 +0100 Subject: NOISSUE add linux distro name and release stats to analytics Hopefully this can serve as some sort of guideline for focusing effort towards the right distro packages to make. --- libraries/systeminfo/CMakeLists.txt | 2 + libraries/systeminfo/include/distroutils.h | 23 +++ libraries/systeminfo/include/sys.h | 29 +++ libraries/systeminfo/src/distroutils.cpp | 283 +++++++++++++++++++++++++++++ libraries/systeminfo/src/sys_apple.cpp | 6 + libraries/systeminfo/src/sys_test.cpp | 8 + libraries/systeminfo/src/sys_unix.cpp | 26 +++ libraries/systeminfo/src/sys_win32.cpp | 6 + 8 files changed, 383 insertions(+) create mode 100644 libraries/systeminfo/include/distroutils.h create mode 100644 libraries/systeminfo/src/distroutils.cpp (limited to 'libraries/systeminfo') diff --git a/libraries/systeminfo/CMakeLists.txt b/libraries/systeminfo/CMakeLists.txt index 77cba173..393b5318 100644 --- a/libraries/systeminfo/CMakeLists.txt +++ b/libraries/systeminfo/CMakeLists.txt @@ -4,6 +4,8 @@ find_package(Qt5Core) set(systeminfo_SOURCES include/sys.h +include/distroutils.h +src/distroutils.cpp ) if (WIN32) diff --git a/libraries/systeminfo/include/distroutils.h b/libraries/systeminfo/include/distroutils.h new file mode 100644 index 00000000..5ff8d591 --- /dev/null +++ b/libraries/systeminfo/include/distroutils.h @@ -0,0 +1,23 @@ +#include "sys.h" +#include + +namespace Sys { +struct LsbInfo +{ + QString distributor; + QString version; + QString description; + QString codename; +}; + +bool main_lsb_info(LsbInfo & out); +bool fallback_lsb_info(Sys::LsbInfo & out); +void lsb_postprocess(Sys::LsbInfo & lsb, Sys::DistributionInfo & out); +Sys::DistributionInfo read_lsb_release(); + +QString _extract_distribution(const QString & x); +QString _extract_version(const QString & x); +Sys::DistributionInfo read_legacy_release(); + +Sys::DistributionInfo read_os_release(); +} diff --git a/libraries/systeminfo/include/sys.h b/libraries/systeminfo/include/sys.h index e40d9a92..c573eb53 100644 --- a/libraries/systeminfo/include/sys.h +++ b/libraries/systeminfo/include/sys.h @@ -12,6 +12,35 @@ struct KernelInfo KernelInfo getKernelInfo(); +struct DistributionInfo +{ + DistributionInfo operator+(const DistributionInfo& rhs) const + { + DistributionInfo out; + if(!distributionName.isEmpty()) + { + out.distributionName = distributionName; + } + else + { + out.distributionName = rhs.distributionName; + } + if(!distributionVersion.isEmpty()) + { + out.distributionVersion = distributionVersion; + } + else + { + out.distributionVersion = rhs.distributionVersion; + } + return out; + } + QString distributionName; + QString distributionVersion; +}; + +DistributionInfo getDistributionInfo(); + uint64_t getSystemRam(); bool isSystem64bit(); diff --git a/libraries/systeminfo/src/distroutils.cpp b/libraries/systeminfo/src/distroutils.cpp new file mode 100644 index 00000000..cdba05d0 --- /dev/null +++ b/libraries/systeminfo/src/distroutils.cpp @@ -0,0 +1,283 @@ +/* + +Code has been taken from https://github.com/natefoo/lionshead and loosely +translated to C++ laced with Qt. + +MIT License + +Copyright (c) 2017 Nate Coraor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +#include "distroutils.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +Sys::DistributionInfo Sys::read_os_release() +{ + Sys::DistributionInfo out; + QStringList files = { "/etc/os-release", "/usr/lib/os-release" }; + QString name; + QString version; + for (auto &file: files) + { + if(!QFile::exists(file)) + { + continue; + } + QSettings settings(file, QSettings::IniFormat); + if(settings.contains("ID")) + { + name = settings.value("ID").toString().toLower(); + } + else if (settings.contains("NAME")) + { + name = settings.value("NAME").toString().toLower(); + } + else + { + continue; + } + + if(settings.contains("VERSION_ID")) + { + version = settings.value("VERSION_ID").toString().toLower(); + } + else if(settings.contains("VERSION")) + { + version = settings.value("VERSION").toString().toLower(); + } + break; + } + if(name.isEmpty()) + { + return out; + } + out.distributionName = name; + out.distributionVersion = version; + return out; +} + +bool Sys::main_lsb_info(Sys::LsbInfo & out) +{ + int status=0; + QProcess lsbProcess; + lsbProcess.start("lsb_release -a"); + lsbProcess.waitForFinished(); + status = lsbProcess.exitStatus(); + QString output = lsbProcess.readAllStandardOutput(); + qDebug() << output; + lsbProcess.close(); + if(status == 0) + { + auto lines = output.split('\n'); + for(auto line:lines) + { + int index = line.indexOf(':'); + auto key = line.left(index).trimmed(); + auto value = line.mid(index + 1).toLower().trimmed(); + if(key == "Distributor ID") + out.distributor = value; + else if(key == "Release") + out.version = value; + else if(key == "Description") + out.description = value; + else if(key == "Codename") + out.codename = value; + } + return !out.distributor.isEmpty(); + } + return false; +} + +bool Sys::fallback_lsb_info(Sys::LsbInfo & out) +{ + // running lsb_release failed, try to read the file instead + // /etc/lsb-release format, if the file even exists, is non-standard. + // Only the `lsb_release` command is specified by LSB. Nonetheless, some + // distributions install an /etc/lsb-release as part of the base + // distribution, but `lsb_release` remains optional. + QString file = "/etc/lsb-release"; + if (QFile::exists(file)) + { + QSettings settings(file, QSettings::IniFormat); + if(settings.contains("DISTRIB_ID")) + { + out.distributor = settings.value("DISTRIB_ID").toString().toLower(); + } + if(settings.contains("DISTRIB_RELEASE")) + { + out.version = settings.value("DISTRIB_RELEASE").toString().toLower(); + } + return !out.distributor.isEmpty(); + } + return false; +} + +void Sys::lsb_postprocess(Sys::LsbInfo & lsb, Sys::DistributionInfo & out) +{ + QString dist = lsb.distributor; + QString vers = lsb.version; + if(dist.startsWith("redhatenterprise")) + { + dist = "rhel"; + } + else if(dist == "archlinux") + { + dist = "arch"; + } + else if (dist.startsWith("suse")) + { + if(lsb.description.startsWith("opensuse")) + { + dist = "opensuse"; + } + else if (lsb.description.startsWith("suse linux enterprise")) + { + dist = "sles"; + } + } + else if (dist == "debian" and vers == "testing") + { + vers = lsb.codename; + } + else + { + // ubuntu, debian, gentoo, scientific, slackware, ... ? + auto parts = dist.split(QRegExp("\\s+"), QString::SkipEmptyParts); + if(parts.size()) + { + dist = parts[0]; + } + } + if(!dist.isEmpty()) + { + out.distributionName = dist; + out.distributionVersion = vers; + } +} + +Sys::DistributionInfo Sys::read_lsb_release() +{ + LsbInfo lsb; + if(!main_lsb_info(lsb)) + { + if(!fallback_lsb_info(lsb)) + { + return Sys::DistributionInfo(); + } + } + Sys::DistributionInfo out; + lsb_postprocess(lsb, out); + return out; +} + +QString Sys::_extract_distribution(const QString & x) +{ + QString release = x.toLower(); + if (release.startsWith("red hat enterprise")) + { + return "rhel"; + } + if (release.startsWith("suse linux enterprise")) + { + return "sles"; + } + QStringList list = release.split(QRegExp("\\s+"), QString::SkipEmptyParts); + if(list.size()) + { + return list[0]; + } + return QString(); +} + +QString Sys::_extract_version(const QString & x) +{ + QRegExp versionish_string("\\d+(?:\\.\\d+)*$"); + QStringList list = x.split(QRegExp("\\s+"), QString::SkipEmptyParts); + for(int i = list.size() - 1; i >= 0; --i) + { + QString chunk = list[i]; + if(versionish_string.exactMatch(chunk)) + { + return chunk; + } + } + return QString(); +} + +Sys::DistributionInfo Sys::read_legacy_release() +{ + struct checkEntry + { + QString file; + std::function extract_distro; + std::function extract_version; + }; + QList checks = + { + {"/etc/arch-release", [](const QString &){ return "arch";}, [](const QString &){ return "rolling";}}, + {"/etc/slackware-version", &Sys::_extract_distribution, &Sys::_extract_version}, + {QString(), &Sys::_extract_distribution, &Sys::_extract_version}, + {"/etc/debian_version", [](const QString &){ return "debian";}, [](const QString & x){ return x;}}, + }; + for(auto & check: checks) + { + QStringList files; + if(check.file.isNull()) + { + QDir etcDir("/etc"); + etcDir.setNameFilters({"*-release"}); + etcDir.setFilter(QDir::Files | QDir::NoDot | QDir::NoDotDot | QDir::Readable | QDir::Hidden); + files = etcDir.entryList(); + } + else + { + files.append(check.file); + } + for (auto file : files) + { + QFile relfile(file); + if(!relfile.open(QIODevice::ReadOnly | QIODevice::Text)) + continue; + QString contents = QString::fromUtf8(relfile.readLine()).trimmed(); + QString dist = check.extract_distro(contents); + QString vers = check.extract_version(contents); + if(!dist.isEmpty()) + { + Sys::DistributionInfo out; + out.distributionName = dist; + out.distributionVersion = vers; + return out; + } + } + } + return Sys::DistributionInfo(); +} + diff --git a/libraries/systeminfo/src/sys_apple.cpp b/libraries/systeminfo/src/sys_apple.cpp index 49a61165..62e6037d 100644 --- a/libraries/systeminfo/src/sys_apple.cpp +++ b/libraries/systeminfo/src/sys_apple.cpp @@ -39,3 +39,9 @@ bool Sys::isSystem64bit() // yep. maybe when we have 128bit CPUs on consumer devices. return true; } + +Sys::DistributionInfo Sys::getDistributionInfo() +{ + DistributionInfo result; + return result; +} diff --git a/libraries/systeminfo/src/sys_test.cpp b/libraries/systeminfo/src/sys_test.cpp index 6221da45..0f6007c9 100644 --- a/libraries/systeminfo/src/sys_test.cpp +++ b/libraries/systeminfo/src/sys_test.cpp @@ -15,6 +15,14 @@ slots: QVERIFY(!kinfo.kernelName.isEmpty()); QVERIFY(kinfo.kernelVersion != "0.0"); } + + void test_systemDistroNotNull() + { + auto kinfo = Sys::getDistributionInfo(); + QVERIFY(!kinfo.distributionName.isEmpty()); + QVERIFY(!kinfo.distributionVersion.isEmpty()); + qDebug() << "Distro: " << kinfo.distributionName << "version" << kinfo.distributionVersion; + } }; QTEST_GUILESS_MAIN(SysTest) diff --git a/libraries/systeminfo/src/sys_unix.cpp b/libraries/systeminfo/src/sys_unix.cpp index 866c9fdb..313908f3 100644 --- a/libraries/systeminfo/src/sys_unix.cpp +++ b/libraries/systeminfo/src/sys_unix.cpp @@ -1,5 +1,7 @@ #include "sys.h" +#include "distroutils.h" + #include #include @@ -47,3 +49,27 @@ bool Sys::isSystem64bit() // kernel build arch on linux return QSysInfo::currentCpuArchitecture() == "x86_64"; } + +Sys::DistributionInfo Sys::getDistributionInfo() +{ + DistributionInfo systemd_info = read_os_release(); + DistributionInfo lsb_info = read_lsb_release(); + DistributionInfo legacy_info = read_legacy_release(); + DistributionInfo result = systemd_info + lsb_info + legacy_info; + if(result.distributionName.isNull()) + { + result.distributionName = "unknown"; + } + if(result.distributionVersion.isNull()) + { + if(result.distributionName == "arch") + { + result.distributionVersion = "rolling"; + } + else + { + result.distributionVersion = "unknown"; + } + } + return result; +} diff --git a/libraries/systeminfo/src/sys_win32.cpp b/libraries/systeminfo/src/sys_win32.cpp index 502b980d..cc1d61c1 100644 --- a/libraries/systeminfo/src/sys_win32.cpp +++ b/libraries/systeminfo/src/sys_win32.cpp @@ -44,3 +44,9 @@ bool Sys::isCPU64bit() auto arch = info.wProcessorArchitecture; return arch == PROCESSOR_ARCHITECTURE_AMD64 || arch == PROCESSOR_ARCHITECTURE_IA64; } + +Sys::DistributionInfo Sys::getDistributionInfo() +{ + DistributionInfo result; + return result; +} -- cgit v1.2.3