File indexing completed on 2024-04-28 15:20:31
0001 /* 0002 SPDX-FileCopyrightText: 2020 Kai Uwe Broulik <kde@broulik.de> 0003 SPDX-FileCopyrightText: 2021 David Edmundson <davidedmundson@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "updatelaunchenvironmentjob.h" 0009 0010 #include <QDBusArgument> 0011 #include <QDBusConnection> 0012 #include <QDBusMetaType> 0013 #include <QDBusPendingReply> 0014 0015 #include <QTimer> 0016 0017 #include "kdbusaddons_debug.h" 0018 0019 class UpdateLaunchEnvironmentJobPrivate 0020 { 0021 public: 0022 explicit UpdateLaunchEnvironmentJobPrivate(UpdateLaunchEnvironmentJob *q); 0023 void monitorReply(const QDBusPendingReply<> &reply); 0024 0025 static bool isPosixName(const QString &name); 0026 static bool isSystemdApprovedValue(const QString &value); 0027 0028 UpdateLaunchEnvironmentJob *q; 0029 QProcessEnvironment environment; 0030 int pendingReplies = 0; 0031 }; 0032 0033 UpdateLaunchEnvironmentJobPrivate::UpdateLaunchEnvironmentJobPrivate(UpdateLaunchEnvironmentJob *q) 0034 : q(q) 0035 { 0036 } 0037 0038 void UpdateLaunchEnvironmentJobPrivate::monitorReply(const QDBusPendingReply<> &reply) 0039 { 0040 ++pendingReplies; 0041 0042 auto *watcher = new QDBusPendingCallWatcher(reply, q); 0043 QObject::connect(watcher, &QDBusPendingCallWatcher::finished, q, [this](QDBusPendingCallWatcher *watcher) { 0044 watcher->deleteLater(); 0045 --pendingReplies; 0046 0047 if (pendingReplies == 0) { 0048 Q_EMIT q->finished(); 0049 q->deleteLater(); 0050 } 0051 }); 0052 } 0053 0054 // KF6 TODO: add K-prefix to class name 0055 UpdateLaunchEnvironmentJob::UpdateLaunchEnvironmentJob(const QProcessEnvironment &environment) 0056 : d(new UpdateLaunchEnvironmentJobPrivate(this)) 0057 { 0058 d->environment = environment; 0059 QTimer::singleShot(0, this, &UpdateLaunchEnvironmentJob::start); 0060 } 0061 0062 UpdateLaunchEnvironmentJob::~UpdateLaunchEnvironmentJob() = default; 0063 0064 void UpdateLaunchEnvironmentJob::start() 0065 { 0066 qDBusRegisterMetaType<QMap<QString, QString>>(); 0067 QMap<QString, QString> dbusActivationEnv; 0068 QStringList systemdUpdates; 0069 0070 for (const auto &varName : d->environment.keys()) { 0071 if (!UpdateLaunchEnvironmentJobPrivate::isPosixName(varName)) { 0072 qCWarning(KDBUSADDONS_LOG) << "Skipping syncing of environment variable " << varName << "as name contains unsupported characters"; 0073 continue; 0074 } 0075 const QString value = d->environment.value(varName); 0076 0077 // KLauncher; remove this in KF6 (by then KInit will be gone) 0078 QDBusMessage klauncherMsg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.klauncher5"), 0079 QStringLiteral("/KLauncher"), 0080 QStringLiteral("org.kde.KLauncher"), 0081 QStringLiteral("setLaunchEnv")); 0082 klauncherMsg.setArguments({QVariant::fromValue(varName), QVariant::fromValue(value)}); 0083 auto klauncherReply = QDBusConnection::sessionBus().asyncCall(klauncherMsg); 0084 d->monitorReply(klauncherReply); 0085 0086 // plasma-session 0087 QDBusMessage plasmaSessionMsg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.Startup"), 0088 QStringLiteral("/Startup"), 0089 QStringLiteral("org.kde.Startup"), 0090 QStringLiteral("updateLaunchEnv")); 0091 plasmaSessionMsg.setArguments({QVariant::fromValue(varName), QVariant::fromValue(value)}); 0092 auto plasmaSessionReply = QDBusConnection::sessionBus().asyncCall(plasmaSessionMsg); 0093 d->monitorReply(plasmaSessionReply); 0094 0095 // DBus-activation environment 0096 dbusActivationEnv.insert(varName, value); 0097 0098 // _user_ systemd env 0099 // Systemd has stricter parsing of valid environment variables 0100 // https://github.com/systemd/systemd/issues/16704 0101 // validate here 0102 if (!UpdateLaunchEnvironmentJobPrivate::isSystemdApprovedValue(value)) { 0103 qCWarning(KDBUSADDONS_LOG) << "Skipping syncing of environment variable " << varName << "as value contains unsupported characters"; 0104 continue; 0105 } 0106 const QString updateString = varName + QStringLiteral("=") + value; 0107 systemdUpdates.append(updateString); 0108 } 0109 0110 // DBus-activation environment 0111 QDBusMessage dbusActivationMsg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DBus"), 0112 QStringLiteral("/org/freedesktop/DBus"), 0113 QStringLiteral("org.freedesktop.DBus"), 0114 QStringLiteral("UpdateActivationEnvironment")); 0115 dbusActivationMsg.setArguments({QVariant::fromValue(dbusActivationEnv)}); 0116 0117 auto dbusActivationReply = QDBusConnection::sessionBus().asyncCall(dbusActivationMsg); 0118 d->monitorReply(dbusActivationReply); 0119 0120 // _user_ systemd env 0121 QDBusMessage systemdActivationMsg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.systemd1"), 0122 QStringLiteral("/org/freedesktop/systemd1"), 0123 QStringLiteral("org.freedesktop.systemd1.Manager"), 0124 QStringLiteral("SetEnvironment")); 0125 systemdActivationMsg.setArguments({systemdUpdates}); 0126 0127 auto systemdActivationReply = QDBusConnection::sessionBus().asyncCall(systemdActivationMsg); 0128 d->monitorReply(systemdActivationReply); 0129 } 0130 0131 bool UpdateLaunchEnvironmentJobPrivate::isPosixName(const QString &name) 0132 { 0133 // Posix says characters like % should be 'tolerated', but it gives issues in practice. 0134 // https://bugzilla.redhat.com/show_bug.cgi?id=1754395 0135 // https://bugzilla.redhat.com/show_bug.cgi?id=1879216 0136 // Ensure systemd compat by only allowing alphanumerics and _ in names. 0137 bool first = true; 0138 for (const QChar c : name) { 0139 if (first && !c.isLetter() && c != QLatin1Char('_')) { 0140 return false; 0141 } else if (first) { 0142 first = false; 0143 } else if (!c.isLetterOrNumber() && c != QLatin1Char('_')) { 0144 return false; 0145 } 0146 } 0147 return !first; 0148 } 0149 0150 bool UpdateLaunchEnvironmentJobPrivate::isSystemdApprovedValue(const QString &value) 0151 { 0152 // systemd code checks that a value contains no control characters except \n \t 0153 // effectively copied from systemd's string_has_cc 0154 for (const char &it : value.toLatin1()) { 0155 if (it == QLatin1Char('\n') || it == QLatin1Char('\t')) { 0156 continue; 0157 } 0158 if (it > 0 && it < ' ') { 0159 return false; 0160 } 0161 if (it == 127) { 0162 return false; 0163 } 0164 } 0165 return true; 0166 } 0167 0168 #include "moc_updatelaunchenvironmentjob.cpp"