File indexing completed on 2024-05-12 17:00:16

0001 /*
0002     SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include "osinfo.h"
0008 
0009 #include <QDBusConnection>
0010 #include <QDBusMessage>
0011 #include <QDBusPendingCallWatcher>
0012 #include <QDBusPendingReply>
0013 
0014 #include <KPluginFactory>
0015 #include <KLocalizedString>
0016 #include <KCoreAddons>
0017 #include <KOSRelease>
0018 
0019 #include <systemstats/SensorContainer.h>
0020 #include <systemstats/SensorObject.h>
0021 #include <systemstats/SensorProperty.h>
0022 
0023 #ifdef Q_OS_LINUX
0024 #include <sys/sysinfo.h>
0025 #endif
0026 #include <time.h>
0027 
0028 // Uppercase the first letter of each word.
0029 QString upperCaseFirst(const QString &input)
0030 {
0031     auto parts = input.split(QLatin1Char(' '), Qt::SkipEmptyParts);
0032     for (auto &part : parts) {
0033         part[0] = part[0].toUpper();
0034     }
0035     return parts.join(QLatin1Char(' '));
0036 }
0037 
0038 // Helper to simplify async dbus calls.
0039 template <typename T>
0040 QDBusPendingCallWatcher *dbusCall(
0041     const QDBusConnection &bus,
0042     const QString &service,
0043     const QString &object,
0044     const QString &interface,
0045     const QString &method,
0046     const QVariantList &arguments,
0047     std::function<void(const QDBusPendingReply<T>&)> callback
0048 )
0049 {
0050     auto message = QDBusMessage::createMethodCall(service, object, interface, method);
0051     message.setArguments(arguments);
0052     auto watcher = new QDBusPendingCallWatcher{bus.asyncCall(message)};
0053     QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher, [callback](QDBusPendingCallWatcher *watcher) {
0054         QDBusPendingReply<T> reply = watcher->reply();
0055         callback(reply);
0056         watcher->deleteLater();
0057     });
0058     return watcher;
0059 }
0060 
0061 class OSInfoPrivate
0062 {
0063 public:
0064     OSInfoPrivate(OSInfoPlugin *qq);
0065     virtual ~OSInfoPrivate() = default;
0066 
0067     virtual void update();
0068     virtual void init();
0069 
0070     OSInfoPlugin *q;
0071 
0072     KSysGuard::SensorContainer *container = nullptr;
0073 
0074     KSysGuard::SensorObject *kernelObject = nullptr;
0075     KSysGuard::SensorProperty *kernelNameProperty = nullptr;
0076     KSysGuard::SensorProperty *kernelVersionProperty = nullptr;
0077     KSysGuard::SensorProperty *kernelPrettyNameProperty = nullptr;
0078 
0079     KSysGuard::SensorObject *systemObject = nullptr;
0080     KSysGuard::SensorProperty *hostnameProperty = nullptr;
0081     KSysGuard::SensorProperty *osNameProperty = nullptr;
0082     KSysGuard::SensorProperty *osVersionProperty = nullptr;
0083     KSysGuard::SensorProperty *osPrettyNameProperty = nullptr;
0084     KSysGuard::SensorProperty *osLogoProperty = nullptr;
0085     KSysGuard::SensorProperty *osUrlProperty = nullptr;
0086     KSysGuard::SensorProperty *uptimeProperty = nullptr;
0087 
0088     KSysGuard::SensorObject *plasmaObject = nullptr;
0089     KSysGuard::SensorProperty *qtVersionProperty = nullptr;
0090     KSysGuard::SensorProperty *kfVersionProperty = nullptr;
0091     KSysGuard::SensorProperty *plasmaVersionProperty = nullptr;
0092     KSysGuard::SensorProperty *windowSystemProperty = nullptr;
0093 };
0094 
0095 class LinuxPrivate : public OSInfoPrivate
0096 {
0097 public:
0098     LinuxPrivate(OSInfoPlugin *qq) : OSInfoPrivate(qq) { }
0099 
0100     void init() override;
0101 };
0102 
0103 OSInfoPrivate::OSInfoPrivate(OSInfoPlugin *qq)
0104     : q(qq)
0105 {
0106     container = new KSysGuard::SensorContainer(QStringLiteral("os"), i18nc("@title", "Operating System"), q);
0107 
0108     kernelObject = new KSysGuard::SensorObject(QStringLiteral("kernel"), i18nc("@title", "Kernel"), container);
0109     kernelNameProperty = new KSysGuard::SensorProperty(QStringLiteral("name"), i18nc("@title", "Kernel Name"), kernelObject);
0110     kernelVersionProperty = new KSysGuard::SensorProperty(QStringLiteral("version"), i18nc("@title", "Kernel Version"), kernelObject);
0111     kernelPrettyNameProperty = new KSysGuard::SensorProperty(QStringLiteral("prettyName"), i18nc("@title", "Kernel Name and Version"), kernelObject);
0112     kernelPrettyNameProperty->setShortName(i18nc("@title Kernel Name and Version", "Kernel"));
0113 
0114     systemObject = new KSysGuard::SensorObject(QStringLiteral("system"), i18nc("@title", "System"), container);
0115     hostnameProperty = new KSysGuard::SensorProperty(QStringLiteral("hostname"), i18nc("@title", "Hostname"), systemObject);
0116     osNameProperty = new KSysGuard::SensorProperty(QStringLiteral("name"), i18nc("@title", "Operating System Name"), systemObject);
0117     osVersionProperty = new KSysGuard::SensorProperty(QStringLiteral("version"), i18nc("@title", "Operating System Version"), systemObject);
0118     osPrettyNameProperty = new KSysGuard::SensorProperty(QStringLiteral("prettyName"), i18nc("@title", "Operating System Name and Version"), systemObject);
0119     osPrettyNameProperty->setShortName(i18nc("@title Operating System Name and Version", "OS"));
0120     osLogoProperty = new KSysGuard::SensorProperty(QStringLiteral("logo"), i18nc("@title", "Operating System Logo"), systemObject);
0121     osUrlProperty = new KSysGuard::SensorProperty(QStringLiteral("url"), i18nc("@title", "Operating System URL"), systemObject);
0122     uptimeProperty = new KSysGuard::SensorProperty(QStringLiteral("uptime"), i18nc("@title", "Uptime"), systemObject);
0123     uptimeProperty->setUnit(KSysGuard::UnitTime);
0124 
0125     plasmaObject = new KSysGuard::SensorObject(QStringLiteral("plasma"), i18nc("@title", "KDE Plasma"), container);
0126     qtVersionProperty = new KSysGuard::SensorProperty(QStringLiteral("qtVersion"), i18nc("@title", "Qt Version"), plasmaObject);
0127     kfVersionProperty = new KSysGuard::SensorProperty(QStringLiteral("kfVersion"), i18nc("@title", "KDE Frameworks Version"), plasmaObject);
0128     plasmaVersionProperty = new KSysGuard::SensorProperty(QStringLiteral("plasmaVersion"), i18nc("@title", "KDE Plasma Version"), plasmaObject);
0129     windowSystemProperty = new KSysGuard::SensorProperty(QStringLiteral("windowsystem"), i18nc("@title", "Window System"), plasmaObject);
0130 }
0131 
0132 OSInfoPlugin::~OSInfoPlugin() = default;
0133 
0134 void OSInfoPrivate::init()
0135 {
0136     auto kernelName = upperCaseFirst(QSysInfo::kernelType());
0137     kernelNameProperty->setValue(kernelName);
0138     kernelVersionProperty->setValue(QSysInfo::kernelVersion());
0139     kernelPrettyNameProperty->setValue(QString{kernelName % QLatin1Char(' ') % QSysInfo::kernelVersion()});
0140     hostnameProperty->setValue(QSysInfo::machineHostName());
0141 
0142     KOSRelease os;
0143     osNameProperty->setValue(os.name());
0144     osVersionProperty->setValue(os.version());
0145     osPrettyNameProperty->setValue(os.prettyName());
0146     osLogoProperty->setValue(os.logo());
0147     osUrlProperty->setValue(os.homeUrl());
0148 
0149     qtVersionProperty->setValue(QString::fromLatin1(qVersion()));
0150     kfVersionProperty->setValue(KCoreAddons::versionString());
0151     windowSystemProperty->setValue(qgetenv("XDG_SESSION_TYPE").compare("x11", Qt::CaseInsensitive) == 0 ? QStringLiteral("X11") : QStringLiteral("Wayland"));
0152 
0153     dbusCall<QVariant>(
0154         QDBusConnection::sessionBus(),
0155         QStringLiteral("org.kde.plasmashell"),
0156         QStringLiteral("/MainApplication"),
0157         QStringLiteral("org.freedesktop.DBus.Properties"),
0158         QStringLiteral("Get"),
0159         { QStringLiteral("org.qtproject.Qt.QCoreApplication"), QStringLiteral("applicationVersion") },
0160         [this](const QDBusPendingReply<QVariant> &reply) {
0161             if (reply.isError()) {
0162                 qWarning() << "Could not determine Plasma version, got: " << reply.error().message();
0163                 plasmaVersionProperty->setValue(i18nc("@info", "Unknown"));
0164             } else {
0165                 plasmaVersionProperty->setValue(reply.value());
0166             }
0167         }
0168     );
0169 }
0170 
0171 void OSInfoPrivate::update()
0172 {
0173 #if defined Q_OS_LINUX
0174     struct sysinfo info;
0175     sysinfo(&info);
0176     // can't send a long over the bus
0177     uptimeProperty->setValue(QVariant::fromValue<qlonglong>(info.uptime));
0178 #elif defined Q_OS_FREEBSD
0179     timespec time;
0180     clock_gettime(CLOCK_UPTIME, &time);
0181     uptimeProperty->setValue(QVariant::fromValue<qlonglong>(time.tv_sec));
0182 #endif
0183 }
0184 
0185 void LinuxPrivate::init()
0186 {
0187     OSInfoPrivate::init();
0188 
0189     // Override some properties with values from hostnamed, if available.
0190     dbusCall<QVariantMap>(
0191         QDBusConnection::systemBus(),
0192         QStringLiteral("org.freedesktop.hostname1"),
0193         QStringLiteral("/org/freedesktop/hostname1"),
0194         QStringLiteral("org.freedesktop.DBus.Properties"),
0195         QStringLiteral("GetAll"),
0196         { QStringLiteral("org.freedesktop.hostname1") },
0197         [this](const QDBusPendingReply<QVariantMap> &reply) {
0198             if (reply.isError()) {
0199                 qWarning() << "Could not contact hostnamed, got: " << reply.error().message();
0200             } else {
0201                 auto properties = reply.value();
0202                 auto kernelName = properties.value(QStringLiteral("KernelName"), kernelNameProperty->value()).toString();
0203                 kernelNameProperty->setValue(kernelName);
0204                 auto kernelVersion = properties.value(QStringLiteral("KernelRelease"), kernelVersionProperty->value()).toString();
0205                 kernelVersionProperty->setValue(kernelVersion);
0206                 kernelPrettyNameProperty->setValue(QString{kernelName % QLatin1Char(' ') % kernelVersion});
0207 
0208                 auto prettyHostName = properties.value(QStringLiteral("PrettyHostname"), QString{}).toString();
0209                 if (!prettyHostName.isEmpty()) {
0210                     hostnameProperty->setValue(prettyHostName);
0211                 } else {
0212                     hostnameProperty->setValue(properties.value(QStringLiteral("Hostname"), hostnameProperty->value()));
0213                 }
0214             }
0215         }
0216     );
0217 }
0218 
0219 OSInfoPlugin::OSInfoPlugin(QObject *parent, const QVariantList &args)
0220     : SensorPlugin(parent, args)
0221 {
0222 #ifdef Q_OS_LINUX
0223     d = std::make_unique<LinuxPrivate>(this);
0224 #else
0225     d = std::make_unique<OSInfoPrivate>(this);
0226 #endif
0227     d->init();
0228 }
0229 
0230 void OSInfoPlugin::update()
0231 {
0232     d->update();
0233 }
0234 
0235 K_PLUGIN_CLASS_WITH_JSON(OSInfoPlugin, "metadata.json")
0236 
0237 #include "osinfo.moc"