File indexing completed on 2024-04-14 15:32:48

0001 /*******************************************************************
0002  * systeminformation.cpp
0003  * SPDX-FileCopyrightText: 2009 Dario Andres Rodriguez <andresbajotierra@gmail.com>
0004  * SPDX-FileCopyrightText: 2009 George Kiagiadakis <gkiagia@users.sourceforge.net>
0005  * SPDX-FileCopyrightText: 2019-2022 Harald Sitter <sitter@kde.org>
0006  *
0007  * SPDX-License-Identifier: GPL-2.0-or-later
0008  *
0009  ******************************************************************/
0010 
0011 #include <config-drkonqi.h>
0012 
0013 #include "systeminformation.h"
0014 
0015 #if HAVE_UNAME
0016 #include <errno.h>
0017 #include <sys/utsname.h>
0018 #endif
0019 
0020 #include "drkonqi_debug.h"
0021 #include <KConfig>
0022 #include <KConfigGroup>
0023 #include <KCoreAddons>
0024 #include <KOSRelease>
0025 #include <KProcess>
0026 #include <KSharedConfig>
0027 #include <KWindowSystem>
0028 #include <QStandardPaths>
0029 #include <kcoreaddons_version.h>
0030 
0031 static const QString OS_UNSPECIFIED = QStringLiteral("unspecified");
0032 static const QString PLATFORM_UNSPECIFIED = QStringLiteral("unspecified");
0033 
0034 // This function maps the operating system to an OS value that is
0035 // accepted by bugs.kde.org. If the values change on the server
0036 // side, they need to be updated here as well.
0037 static QString fetchBasicOperatingSystem()
0038 {
0039     // krazy:excludeall=cpp
0040     // Get the base OS string (bugzillaOS)
0041 #if defined(Q_OS_LINUX)
0042     return QStringLiteral("Linux");
0043 #elif defined(Q_OS_FREEBSD)
0044     return QStringLiteral("FreeBSD");
0045 #elif defined(Q_OS_NETBSD)
0046     return QStringLiteral("NetBSD");
0047 #elif defined(Q_OS_OPENBSD)
0048     return QStringLiteral("OpenBSD");
0049 #elif defined(Q_OS_AIX)
0050     return QStringLiteral("AIX");
0051 #elif defined(Q_OS_HPUX)
0052     return QStringLiteral("HP-UX");
0053 #elif defined(Q_OS_IRIX)
0054     return QStringLiteral("IRIX");
0055 #elif defined(Q_OS_OSF)
0056     return QStringLiteral("Tru64");
0057 #elif defined(Q_OS_SOLARIS)
0058     return QStringLiteral("Solaris");
0059 #elif defined(Q_OS_CYGWIN)
0060     return QStringLiteral("Cygwin");
0061 #elif defined(Q_OS_DARWIN)
0062     return QStringLiteral("OS X");
0063 #elif defined(Q_OS_WIN32)
0064     return QStringLiteral("MS Windows");
0065 #else
0066     return OS_UNSPECIFIED;
0067 #endif
0068 }
0069 
0070 SystemInformation::Config::Config()
0071     : basicOperatingSystem(fetchBasicOperatingSystem())
0072     , lsbReleasePath(QStandardPaths::findExecutable(QStringLiteral("lsb_release")))
0073     , osReleasePath(/* Use KOSRelease default */)
0074 {
0075 }
0076 
0077 SystemInformation::SystemInformation(Config infoConfig, QObject *parent)
0078     : QObject(parent)
0079     , m_bugzillaOperatingSystem(infoConfig.basicOperatingSystem)
0080     , m_bugzillaPlatform(PLATFORM_UNSPECIFIED)
0081     , m_complete(false)
0082     , m_infoConfig(infoConfig)
0083 {
0084     // NOTE: order matters. These require m_bugzillaOperatingSystem to be set!
0085     m_operatingSystem = fetchOSDetailInformation();
0086     tryToSetBugzillaPlatform();
0087 
0088     KConfigGroup config(KSharedConfig::openConfig(), "SystemInformation");
0089     m_compiledSources = config.readEntry("CompiledSources", false);
0090 }
0091 
0092 SystemInformation::~SystemInformation()
0093 {
0094     KConfigGroup config(KSharedConfig::openConfig(), "SystemInformation");
0095     config.writeEntry("CompiledSources", m_compiledSources);
0096     config.sync();
0097 }
0098 
0099 void SystemInformation::tryToSetBugzillaPlatform()
0100 {
0101     QString platform = PLATFORM_UNSPECIFIED;
0102     // first, try to guess bugzilla platfrom from the internal OS information
0103     // this should work for BSDs, solaris and windows.
0104     platform = guessBugzillaPlatform(m_bugzillaOperatingSystem);
0105 
0106     // if the internal information is not enough, refer to external information
0107     if (platform == PLATFORM_UNSPECIFIED) {
0108         tryToSetBugzillaPlatformFromExternalInfo();
0109     } else {
0110         setBugzillaPlatform(platform);
0111     }
0112 }
0113 
0114 void SystemInformation::tryToSetBugzillaPlatformFromExternalInfo()
0115 {
0116     // Run lsb_release async
0117     QString lsb_release = m_infoConfig.lsbReleasePath;
0118     if (!lsb_release.isEmpty()) {
0119         qCDebug(DRKONQI_LOG) << "found lsb_release";
0120         auto *process = new KProcess();
0121         process->setOutputChannelMode(KProcess::OnlyStdoutChannel);
0122         process->setEnv(QStringLiteral("LC_ALL"), QStringLiteral("C"));
0123         *process << lsb_release << QStringLiteral("-sd");
0124         connect(process, static_cast<void (KProcess::*)(int, QProcess::ExitStatus)>(&KProcess::finished), this, &SystemInformation::lsbReleaseFinished);
0125         process->start();
0126     } else {
0127         // when lsb_release is unavailable, turn to /etc/os-release
0128         const QString &osReleaseInfo = fetchOSReleaseInformation();
0129         const QString &platform = guessBugzillaPlatform(osReleaseInfo);
0130         setBugzillaPlatform(platform);
0131         m_complete = true;
0132     }
0133 }
0134 
0135 void SystemInformation::lsbReleaseFinished()
0136 {
0137     auto *process = qobject_cast<KProcess *>(sender());
0138     Q_ASSERT(process);
0139     m_distributionPrettyName = QString::fromLocal8Bit(process->readAllStandardOutput().trimmed());
0140     process->deleteLater();
0141 
0142     // Guess distro string
0143     QString platform = guessBugzillaPlatform(m_distributionPrettyName);
0144 
0145     // if lsb_release doesn't work well, turn to the /etc/os-release file
0146     if (platform == PLATFORM_UNSPECIFIED) {
0147         const QString &osReleaseInfo = fetchOSReleaseInformation();
0148         platform = guessBugzillaPlatform(osReleaseInfo);
0149     }
0150 
0151     setBugzillaPlatform(platform);
0152     m_complete = true;
0153 }
0154 
0155 // this function maps the distribution information to an "Hardware Platform"    .
0156 // value that is accepted by bugs.kde.org.  If the values change on the server    .
0157 // side, they need to be updated here as well                                   .
0158 QString SystemInformation::guessBugzillaPlatform(const QString &distroInfo) const
0159 {
0160     static QHash<QString, QString> platforms{{QStringLiteral("suse"), QStringLiteral("openSUSE RPMs")},
0161                                              {QStringLiteral("mint"), QStringLiteral("Mint (Ubuntu Based)")},
0162                                              {QStringLiteral("lmde"), QStringLiteral("Mint (Debian Based)")},
0163                                              {QStringLiteral("ubuntu"), QStringLiteral("Ubuntu Packages")},
0164                                              {QStringLiteral("fedora"), QStringLiteral("Fedora RPMs")},
0165                                              {QStringLiteral("redhat"), QStringLiteral("RedHat RPMs")},
0166                                              {QStringLiteral("gentoo"), QStringLiteral("Gentoo Packages")},
0167                                              {QStringLiteral("mandriva"), QStringLiteral("Mandriva RPMs")},
0168                                              {QStringLiteral("mageia"), QStringLiteral("Mageia RPMs")},
0169                                              {QStringLiteral("slack"), QStringLiteral("Slackware Packages")},
0170                                              {QStringLiteral("pclinuxos"), QStringLiteral("PCLinuxOS")},
0171                                              {QStringLiteral("pardus"), QStringLiteral("Pardus Packages")},
0172                                              {QStringLiteral("freebsd"), QStringLiteral("FreeBSD Ports")},
0173                                              {QStringLiteral("netbsd"), QStringLiteral("NetBSD pkgsrc")},
0174                                              {QStringLiteral("openbsd"), QStringLiteral("OpenBSD Packages")},
0175                                              {QStringLiteral("solaris"), QStringLiteral("Solaris Packages")},
0176                                              {QStringLiteral("chakra"), QStringLiteral("Chakra")},
0177                                              {QStringLiteral("ms windows"), QStringLiteral("MS Windows")},
0178                                              {QStringLiteral("arch"), QStringLiteral("Archlinux Packages")},
0179                                              {QStringLiteral("kde neon"), QStringLiteral("Neon Packages")}};
0180     for (auto it = platforms.constBegin(); it != platforms.constEnd(); ++it) {
0181         if (distroInfo.contains(it.key(), Qt::CaseInsensitive)) {
0182             return it.value();
0183         }
0184     }
0185 
0186     // Debian has multiple platforms.
0187     if (distroInfo.contains(QLatin1String("debian"), Qt::CaseInsensitive)) {
0188         if (distroInfo.contains(QLatin1String("unstable"), Qt::CaseInsensitive)) {
0189             return QStringLiteral("Debian unstable");
0190         } else if (distroInfo.contains(QLatin1String("testing"), Qt::CaseInsensitive)) {
0191             return QStringLiteral("Debian testing");
0192         } else {
0193             return QStringLiteral("Debian stable");
0194         }
0195     }
0196 
0197     return PLATFORM_UNSPECIFIED;
0198 }
0199 
0200 QString SystemInformation::fetchOSDetailInformation() const
0201 {
0202     // Get complete OS string (and fallback to base string)
0203     QString operatingSystem = m_bugzillaOperatingSystem;
0204 
0205 #if HAVE_UNAME
0206     struct utsname buf;
0207 
0208     auto unameFunc = &uname;
0209     if (m_infoConfig.unameFunc) {
0210         unameFunc = (decltype(&uname))m_infoConfig.unameFunc;
0211     }
0212 
0213     if ((*unameFunc)(&buf) == -1) {
0214         qCDebug(DRKONQI_LOG) << "call to uname failed" << errno;
0215     } else {
0216         operatingSystem = QString::fromLocal8Bit(buf.sysname) + QLatin1Char(' ') + QString::fromLocal8Bit(buf.release) + QLatin1Char(' ')
0217             + QString::fromLocal8Bit(buf.machine);
0218     }
0219 #endif
0220 
0221     return operatingSystem;
0222 }
0223 
0224 QString SystemInformation::fetchOSReleaseInformation()
0225 {
0226     KOSRelease os(m_infoConfig.osReleasePath);
0227     return m_distributionPrettyName = os.prettyName();
0228 }
0229 
0230 QString SystemInformation::operatingSystem() const
0231 {
0232     return m_operatingSystem;
0233 }
0234 
0235 QString SystemInformation::bugzillaOperatingSystem() const
0236 {
0237     return m_bugzillaOperatingSystem;
0238 }
0239 
0240 QString SystemInformation::bugzillaPlatform() const
0241 {
0242     return m_bugzillaPlatform;
0243 }
0244 
0245 void SystemInformation::setBugzillaPlatform(const QString &platform)
0246 {
0247     m_bugzillaPlatform = platform;
0248     Q_EMIT bugzillaPlatformChanged();
0249 }
0250 
0251 QString SystemInformation::distributionPrettyName() const
0252 {
0253     return m_distributionPrettyName;
0254 }
0255 
0256 bool SystemInformation::compiledSources() const
0257 {
0258     return m_compiledSources;
0259 }
0260 
0261 void SystemInformation::setCompiledSources(bool compiled)
0262 {
0263     m_compiledSources = compiled;
0264     Q_EMIT compiledSourcesChanged();
0265 }
0266 
0267 QString SystemInformation::qtVersion() const
0268 {
0269     return QString::fromLatin1(qVersion());
0270 }
0271 
0272 QString SystemInformation::frameworksVersion() const
0273 {
0274     return KCoreAddons::versionString();
0275 }
0276 
0277 bool SystemInformation::complete() const
0278 {
0279     return m_complete;
0280 }
0281 
0282 QString SystemInformation::windowSystem() const
0283 {
0284     switch (KWindowSystem::platform()) {
0285     case KWindowSystem::Platform::Unknown:
0286         return QStringLiteral("Unknown");
0287     case KWindowSystem::Platform::X11:
0288         return QStringLiteral("X11");
0289     case KWindowSystem::Platform::Wayland:
0290         return QStringLiteral("Wayland");
0291     }
0292     return QStringLiteral("Unknown");
0293 }